Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 8441657

Browse files
MarcoRossignolidanmoseley
authored andcommitted
improve entropy (#23591)
1 parent d8efb7d commit 8441657

File tree

1 file changed

+48
-40
lines changed
  • src/System.Private.CoreLib/shared/System/Collections/Generic

1 file changed

+48
-40
lines changed

src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Diagnostics;
66
using System.Runtime.CompilerServices;
77
using System.Runtime.Serialization;
8-
using System.Threading;
98

109
namespace System.Collections.Generic
1110
{
@@ -38,8 +37,11 @@ public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary,
3837
{
3938
private struct Entry
4039
{
41-
public int hashCode; // Lower 31 bits of hash code, -1 if unused
42-
public int next; // Index of next entry, -1 if last
40+
// 0-based index of next entry in chain: -1 means end of chain
41+
// also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3,
42+
// so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc.
43+
public int next;
44+
public uint hashCode;
4345
public TKey key; // Key of entry
4446
public TValue value; // Value of entry
4547
}
@@ -53,6 +55,7 @@ private struct Entry
5355
private IEqualityComparer<TKey> _comparer;
5456
private KeyCollection _keys;
5557
private ValueCollection _values;
58+
private const int StartOfFreeList = -3;
5659

5760
// constants for serialization
5861
private const string VersionName = "Version"; // Do not rename (binary serialization)
@@ -103,7 +106,7 @@ public Dictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey>
103106
Entry[] entries = d._entries;
104107
for (int i = 0; i < count; i++)
105108
{
106-
if (entries[i].hashCode >= 0)
109+
if (entries[i].next >= -1)
107110
{
108111
Add(entries[i].key, entries[i].value);
109112
}
@@ -278,7 +281,7 @@ public bool ContainsValue(TValue value)
278281
{
279282
for (int i = 0; i < _count; i++)
280283
{
281-
if (entries[i].hashCode >= 0 && entries[i].value == null) return true;
284+
if (entries[i].next >= -1 && entries[i].value == null) return true;
282285
}
283286
}
284287
else
@@ -288,7 +291,7 @@ public bool ContainsValue(TValue value)
288291
// ValueType: Devirtualize with EqualityComparer<TValue>.Default intrinsic
289292
for (int i = 0; i < _count; i++)
290293
{
291-
if (entries[i].hashCode >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, value)) return true;
294+
if (entries[i].next >= -1 && EqualityComparer<TValue>.Default.Equals(entries[i].value, value)) return true;
292295
}
293296
}
294297
else
@@ -299,7 +302,7 @@ public bool ContainsValue(TValue value)
299302
EqualityComparer<TValue> defaultComparer = EqualityComparer<TValue>.Default;
300303
for (int i = 0; i < _count; i++)
301304
{
302-
if (entries[i].hashCode >= 0 && defaultComparer.Equals(entries[i].value, value)) return true;
305+
if (entries[i].next >= -1 && defaultComparer.Equals(entries[i].value, value)) return true;
303306
}
304307
}
305308
}
@@ -327,7 +330,7 @@ private void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
327330
Entry[] entries = _entries;
328331
for (int i = 0; i < count; i++)
329332
{
330-
if (entries[i].hashCode >= 0)
333+
if (entries[i].next >= -1)
331334
{
332335
array[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
333336
}
@@ -375,9 +378,9 @@ private int FindEntry(TKey key)
375378
IEqualityComparer<TKey> comparer = _comparer;
376379
if (comparer == null)
377380
{
378-
int hashCode = key.GetHashCode() & 0x7FFFFFFF;
381+
uint hashCode = (uint)key.GetHashCode();
379382
// Value in _buckets is 1-based
380-
i = buckets[hashCode % buckets.Length] - 1;
383+
i = buckets[hashCode % (uint)buckets.Length] - 1;
381384
if (default(TKey) != null)
382385
{
383386
// ValueType: Devirtualize with EqualityComparer<TValue>.Default intrinsic
@@ -428,9 +431,9 @@ private int FindEntry(TKey key)
428431
}
429432
else
430433
{
431-
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
434+
uint hashCode = (uint)comparer.GetHashCode(key);
432435
// Value in _buckets is 1-based
433-
i = buckets[hashCode % buckets.Length] - 1;
436+
i = buckets[hashCode % (uint)buckets.Length] - 1;
434437
do
435438
{
436439
// Should be a while loop https://github.com/dotnet/coreclr/issues/15476
@@ -482,10 +485,10 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
482485
Entry[] entries = _entries;
483486
IEqualityComparer<TKey> comparer = _comparer;
484487

485-
int hashCode = ((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key)) & 0x7FFFFFFF;
488+
uint hashCode = (uint)((comparer == null) ? key.GetHashCode() : comparer.GetHashCode(key));
486489

487490
int collisionCount = 0;
488-
ref int bucket = ref _buckets[hashCode % _buckets.Length];
491+
ref int bucket = ref _buckets[hashCode % (uint)_buckets.Length];
489492
// Value in _buckets is 1-based
490493
int i = bucket - 1;
491494

@@ -627,7 +630,7 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
627630
if (count == entries.Length)
628631
{
629632
Resize();
630-
bucket = ref _buckets[hashCode % _buckets.Length];
633+
bucket = ref _buckets[hashCode % (uint)_buckets.Length];
631634
}
632635
index = count;
633636
_count = count + 1;
@@ -638,7 +641,9 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
638641

639642
if (updateFreeList)
640643
{
641-
_freeList = entry.next;
644+
Debug.Assert((StartOfFreeList - entries[_freeList].next) >= -1, "shouldn't overflow because `next` cannot underflow");
645+
646+
_freeList = StartOfFreeList - entries[_freeList].next;
642647
}
643648
entry.hashCode = hashCode;
644649
// Value in _buckets is 1-based
@@ -725,19 +730,19 @@ private void Resize(int newSize, bool forceNewHashCodes)
725730
{
726731
for (int i = 0; i < count; i++)
727732
{
728-
if (entries[i].hashCode >= 0)
733+
if (entries[i].next >= -1)
729734
{
730735
Debug.Assert(_comparer == null);
731-
entries[i].hashCode = (entries[i].key.GetHashCode() & 0x7FFFFFFF);
736+
entries[i].hashCode = (uint)entries[i].key.GetHashCode();
732737
}
733738
}
734739
}
735740

736741
for (int i = 0; i < count; i++)
737742
{
738-
if (entries[i].hashCode >= 0)
743+
if (entries[i].next >= -1)
739744
{
740-
int bucket = entries[i].hashCode % newSize;
745+
uint bucket = entries[i].hashCode % (uint)newSize;
741746
// Value in _buckets is 1-based
742747
entries[i].next = buckets[bucket] - 1;
743748
// Value in _buckets is 1-based
@@ -764,8 +769,8 @@ public bool Remove(TKey key)
764769
int collisionCount = 0;
765770
if (buckets != null)
766771
{
767-
int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
768-
int bucket = hashCode % buckets.Length;
772+
uint hashCode = (uint)(_comparer?.GetHashCode(key) ?? key.GetHashCode());
773+
uint bucket = hashCode % (uint)buckets.Length;
769774
int last = -1;
770775
// Value in buckets is 1-based
771776
int i = buckets[bucket] - 1;
@@ -784,8 +789,10 @@ public bool Remove(TKey key)
784789
{
785790
entries[last].next = entry.next;
786791
}
787-
entry.hashCode = -1;
788-
entry.next = _freeList;
792+
793+
Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646");
794+
795+
entry.next = StartOfFreeList - _freeList;
789796

790797
if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
791798
{
@@ -829,8 +836,8 @@ public bool Remove(TKey key, out TValue value)
829836
int collisionCount = 0;
830837
if (buckets != null)
831838
{
832-
int hashCode = (_comparer?.GetHashCode(key) ?? key.GetHashCode()) & 0x7FFFFFFF;
833-
int bucket = hashCode % buckets.Length;
839+
uint hashCode = (uint)(_comparer?.GetHashCode(key) ?? key.GetHashCode());
840+
uint bucket = hashCode % (uint)buckets.Length;
834841
int last = -1;
835842
// Value in buckets is 1-based
836843
int i = buckets[bucket] - 1;
@@ -852,8 +859,9 @@ public bool Remove(TKey key, out TValue value)
852859

853860
value = entry.value;
854861

855-
entry.hashCode = -1;
856-
entry.next = _freeList;
862+
Debug.Assert((StartOfFreeList - _freeList) < 0, "shouldn't underflow because max hashtable length is MaxPrimeArrayLength = 0x7FEFFFFD(2146435069) _freelist underflow threshold 2147483646");
863+
864+
entry.next = StartOfFreeList - _freeList;
857865

858866
if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
859867
{
@@ -925,7 +933,7 @@ void ICollection.CopyTo(Array array, int index)
925933
Entry[] entries = _entries;
926934
for (int i = 0; i < _count; i++)
927935
{
928-
if (entries[i].hashCode >= 0)
936+
if (entries[i].next >= -1)
929937
{
930938
dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value);
931939
}
@@ -945,7 +953,7 @@ void ICollection.CopyTo(Array array, int index)
945953
Entry[] entries = _entries;
946954
for (int i = 0; i < count; i++)
947955
{
948-
if (entries[i].hashCode >= 0)
956+
if (entries[i].next >= -1)
949957
{
950958
objects[index++] = new KeyValuePair<TKey, TValue>(entries[i].key, entries[i].value);
951959
}
@@ -1018,12 +1026,12 @@ public void TrimExcess(int capacity)
10181026
int count = 0;
10191027
for (int i = 0; i < oldCount; i++)
10201028
{
1021-
int hashCode = oldEntries[i].hashCode;
1022-
if (hashCode >= 0)
1029+
uint hashCode = oldEntries[i].hashCode;
1030+
if (oldEntries[i].next >= -1)
10231031
{
10241032
ref Entry entry = ref entries[count];
10251033
entry = oldEntries[i];
1026-
int bucket = hashCode % newSize;
1034+
uint bucket = hashCode % (uint)newSize;
10271035
// Value in _buckets is 1-based
10281036
entry.next = buckets[bucket] - 1;
10291037
// Value in _buckets is 1-based
@@ -1179,7 +1187,7 @@ public bool MoveNext()
11791187
{
11801188
ref Entry entry = ref _dictionary._entries[_index++];
11811189

1182-
if (entry.hashCode >= 0)
1190+
if (entry.next >= -1)
11831191
{
11841192
_current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
11851193
return true;
@@ -1307,7 +1315,7 @@ public void CopyTo(TKey[] array, int index)
13071315
Entry[] entries = _dictionary._entries;
13081316
for (int i = 0; i < count; i++)
13091317
{
1310-
if (entries[i].hashCode >= 0) array[index++] = entries[i].key;
1318+
if (entries[i].next >= -1) array[index++] = entries[i].key;
13111319
}
13121320
}
13131321

@@ -1367,7 +1375,7 @@ void ICollection.CopyTo(Array array, int index)
13671375
{
13681376
for (int i = 0; i < count; i++)
13691377
{
1370-
if (entries[i].hashCode >= 0) objects[index++] = entries[i].key;
1378+
if (entries[i].next >= -1) objects[index++] = entries[i].key;
13711379
}
13721380
}
13731381
catch (ArrayTypeMismatchException)
@@ -1411,7 +1419,7 @@ public bool MoveNext()
14111419
{
14121420
ref Entry entry = ref _dictionary._entries[_index++];
14131421

1414-
if (entry.hashCode >= 0)
1422+
if (entry.next >= -1)
14151423
{
14161424
_currentKey = entry.key;
14171425
return true;
@@ -1490,7 +1498,7 @@ public void CopyTo(TValue[] array, int index)
14901498
Entry[] entries = _dictionary._entries;
14911499
for (int i = 0; i < count; i++)
14921500
{
1493-
if (entries[i].hashCode >= 0) array[index++] = entries[i].value;
1501+
if (entries[i].next >= -1) array[index++] = entries[i].value;
14941502
}
14951503
}
14961504

@@ -1550,7 +1558,7 @@ void ICollection.CopyTo(Array array, int index)
15501558
{
15511559
for (int i = 0; i < count; i++)
15521560
{
1553-
if (entries[i].hashCode >= 0) objects[index++] = entries[i].value;
1561+
if (entries[i].next >= -1) objects[index++] = entries[i].value;
15541562
}
15551563
}
15561564
catch (ArrayTypeMismatchException)
@@ -1594,7 +1602,7 @@ public bool MoveNext()
15941602
{
15951603
ref Entry entry = ref _dictionary._entries[_index++];
15961604

1597-
if (entry.hashCode >= 0)
1605+
if (entry.next >= -1)
15981606
{
15991607
_currentValue = entry.value;
16001608
return true;

0 commit comments

Comments
 (0)