using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
-using System.Threading;
namespace System.Collections.Generic
{
{
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
+ // 0-based index of next entry in chain: -1 means end of chain
+ // also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3,
+ // so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc.
+ public int next;
+ public uint hashCode;
public TKey key; // Key of entry
public TValue value; // Value of entry
}
private IEqualityComparer<TKey> _comparer;
private KeyCollection _keys;
private ValueCollection _values;
+ private const int StartOfFreeList = -3;
// constants for serialization
private const string VersionName = "Version"; // Do not rename (binary serialization)
Entry[] entries = d._entries;
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0)
+ if (entries[i].next >= -1)
{
Add(entries[i].key, entries[i].value);
}
{
for (int i = 0; i < _count; i++)
{
- if (entries[i].hashCode >= 0 && entries[i].value == null) return true;
+ if (entries[i].next >= -1 && entries[i].value == null) return true;
}
}
else
// ValueType: Devirtualize with EqualityComparer<TValue>.Default intrinsic
for (int i = 0; i < _count; i++)
{
- if (entries[i].hashCode >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, value)) return true;
+ if (entries[i].next >= -1 && EqualityComparer<TValue>.Default.Equals(entries[i].value, value)) return true;
}
}
else
EqualityComparer<TValue> defaultComparer = EqualityComparer<TValue>.Default;
for (int i = 0; i < _count; i++)
{
- if (entries[i].hashCode >= 0 && defaultComparer.Equals(entries[i].value, value)) return true;
+ if (entries[i].next >= -1 && defaultComparer.Equals(entries[i].value, value)) return true;
}
}
}
Entry[] entries = _entries;
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0)
+ if (entries[i].next >= -1)
{
array[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
}
IEqualityComparer<TKey> comparer = _comparer;
if (comparer == null)
{
- int hashCode = key.GetHashCode() & 0x7FFFFFFF;
+ uint hashCode = (uint)key.GetHashCode();
// Value in _buckets is 1-based
- i = buckets[hashCode % buckets.Length] - 1;
+ i = buckets[hashCode % (uint)buckets.Length] - 1;
if (default(TKey) != null)
{
// ValueType: Devirtualize with EqualityComparer<TValue>.Default intrinsic
}
else
{
- int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
+ uint hashCode = (uint)comparer.GetHashCode(key);
// Value in _buckets is 1-based
- i = buckets[hashCode % buckets.Length] - 1;
+ i = buckets[hashCode % (uint)buckets.Length] - 1;
do
{
// Should be a while loop https://github.com/dotnet/coreclr/issues/15476
Entry[] entries = _entries;
IEqualityComparer<TKey> comparer = _comparer;
- int hashCode = ((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key)) & 0x7FFFFFFF;
+ uint hashCode = (uint)((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key));
int collisionCount = 0;
- ref int bucket = ref _buckets[hashCode % _buckets.Length];
+ ref int bucket = ref _buckets[hashCode % (uint)_buckets.Length];
// Value in _buckets is 1-based
int i = bucket - 1;
if (count == entries.Length)
{
Resize();
- bucket = ref _buckets[hashCode % _buckets.Length];
+ bucket = ref _buckets[hashCode % (uint)_buckets.Length];
}
index = count;
_count = count + 1;
if (updateFreeList)
{
- _freeList = entry.next;
+ Debug.Assert((StartOfFreeList - entries[_freeList].next) >= -1, "shouldn't overflow because `next` cannot underflow");
+
+ _freeList = StartOfFreeList - entries[_freeList].next;
}
entry.hashCode = hashCode;
// Value in _buckets is 1-based
{
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0)
+ if (entries[i].next >= -1)
{
Debug.Assert(_comparer == null);
- entries[i].hashCode = (entries[i].key.GetHashCode() & 0x7FFFFFFF);
+ entries[i].hashCode = (uint)entries[i].key.GetHashCode();
}
}
}
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0)
+ if (entries[i].next >= -1)
{
- int bucket = entries[i].hashCode % newSize;
+ uint bucket = entries[i].hashCode % (uint)newSize;
// Value in _buckets is 1-based
entries[i].next = buckets[bucket] - 1;
// Value in _buckets is 1-based
int collisionCount = 0;
if (buckets != null)
{
- int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
- int bucket = hashCode % buckets.Length;
+ uint hashCode = (uint)(_comparer?.GetHashCode(key) ?? key.GetHashCode());
+ uint bucket = hashCode % (uint)buckets.Length;
int last = -1;
// Value in buckets is 1-based
int i = buckets[bucket] - 1;
{
entries[last].next = entry.next;
}
- entry.hashCode = -1;
- entry.next = _freeList;
+
+ Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646");
+
+ entry.next = StartOfFreeList - _freeList;
if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
{
int collisionCount = 0;
if (buckets != null)
{
- int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
- int bucket = hashCode % buckets.Length;
+ uint hashCode = (uint)(_comparer?.GetHashCode(key) ?? key.GetHashCode());
+ uint bucket = hashCode % (uint)buckets.Length;
int last = -1;
// Value in buckets is 1-based
int i = buckets[bucket] - 1;
value = entry.value;
- entry.hashCode = -1;
- entry.next = _freeList;
+ Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646");
+
+ entry.next = StartOfFreeList - _freeList;
if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
{
Entry[] entries = _entries;
for (int i = 0; i < _count; i++)
{
- if (entries[i].hashCode >= 0)
+ if (entries[i].next >= -1)
{
dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
}
Entry[] entries = _entries;
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0)
+ if (entries[i].next >= -1)
{
objects[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
}
int count = 0;
for (int i = 0; i < oldCount; i++)
{
- int hashCode = oldEntries[i].hashCode;
- if (hashCode >= 0)
+ uint hashCode = oldEntries[i].hashCode;
+ if (oldEntries[i].next >= -1)
{
ref Entry entry = ref entries[count];
entry = oldEntries[i];
- int bucket = hashCode % newSize;
+ uint bucket = hashCode % (uint)newSize;
// Value in _buckets is 1-based
entry.next = buckets[bucket] - 1;
// Value in _buckets is 1-based
{
ref Entry entry = ref _dictionary._entries[_index++];
- if (entry.hashCode >= 0)
+ if (entry.next >= -1)
{
_current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
return true;
Entry[] entries = _dictionary._entries;
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
+ if (entries[i].next >= -1) array[index++] = entries[i].key;
}
}
{
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0) objects[index++] = entries[i].key;
+ if (entries[i].next >= -1) objects[index++] = entries[i].key;
}
}
catch (ArrayTypeMismatchException)
{
ref Entry entry = ref _dictionary._entries[_index++];
- if (entry.hashCode >= 0)
+ if (entry.next >= -1)
{
_currentKey = entry.key;
return true;
Entry[] entries = _dictionary._entries;
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0) array[index++] = entries[i].value;
+ if (entries[i].next >= -1) array[index++] = entries[i].value;
}
}
{
for (int i = 0; i < count; i++)
{
- if (entries[i].hashCode >= 0) objects[index++] = entries[i].value;
+ if (entries[i].next >= -1) objects[index++] = entries[i].value;
}
}
catch (ArrayTypeMismatchException)
{
ref Entry entry = ref _dictionary._entries[_index++];
- if (entry.hashCode >= 0)
+ if (entry.next >= -1)
{
_currentValue = entry.value;
return true;