55using System . Diagnostics ;
66using System . Runtime . CompilerServices ;
77using System . Runtime . Serialization ;
8- using System . Threading ;
98
109namespace 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