@@ -410,17 +410,27 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
410410 }
411411
412412 if ( _buckets == null ) Initialize ( 0 ) ;
413- int hashCode = _comparer . GetHashCode ( key ) & 0x7FFFFFFF ;
414- int targetBucket = hashCode % _buckets . Length ;
413+ IEqualityComparer < TKey > comparer = _comparer ;
414+ int hashCode = comparer . GetHashCode ( key ) & 0x7FFFFFFF ;
415415 int collisionCount = 0 ;
416416
417- for ( int i = _buckets [ targetBucket ] ; i >= 0 ; i = _entries [ i ] . next )
417+ ref int bucket = ref _buckets [ hashCode % _buckets . Length ] ;
418+ int i = bucket ;
419+ Entry [ ] entries = _entries ;
420+ do
418421 {
419- if ( _entries [ i ] . hashCode == hashCode && _comparer . Equals ( _entries [ i ] . key , key ) )
422+ // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
423+ // Test uint in if rather than loop condition to drop range check for following array access
424+ if ( ( uint ) i >= ( uint ) entries . Length )
425+ {
426+ break ;
427+ }
428+
429+ if ( entries [ i ] . hashCode == hashCode && comparer . Equals ( entries [ i ] . key , key ) )
420430 {
421431 if ( behavior == InsertionBehavior . OverwriteExisting )
422432 {
423- _entries [ i ] . value = value ;
433+ entries [ i ] . value = value ;
424434 _version ++ ;
425435 return true ;
426436 }
@@ -432,41 +442,56 @@ private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
432442
433443 return false ;
434444 }
445+
446+ i = entries [ i ] . next ;
435447 collisionCount ++ ;
436- }
448+ } while ( true ) ;
437449
450+ // Can be improved with "Ref Local Reassignment"
451+ // https://github.com/dotnet/csharplang/blob/master/proposals/ref-local-reassignment.md
452+ bool resized = false ;
453+ bool updateFreeList = false ;
438454 int index ;
439455 if ( _freeCount > 0 )
440456 {
441457 index = _freeList ;
442- _freeList = _entries [ index ] . next ;
458+ updateFreeList = true ;
443459 _freeCount -- ;
444460 }
445461 else
446462 {
447- if ( _count == _entries . Length )
463+ int count = _count ;
464+ if ( count == entries . Length )
448465 {
449466 Resize ( ) ;
450- targetBucket = hashCode % _buckets . Length ;
467+ resized = true ;
451468 }
452- index = _count ;
453- _count ++ ;
469+ index = count ;
470+ _count = count + 1 ;
471+ entries = _entries ;
454472 }
455473
456- _entries [ index ] . hashCode = hashCode ;
457- _entries [ index ] . next = _buckets [ targetBucket ] ;
458- _entries [ index ] . key = key ;
459- _entries [ index ] . value = value ;
460- _buckets [ targetBucket ] = index ;
461- _version ++ ;
474+ ref int targetBucket = ref resized ? ref _buckets [ hashCode % _buckets . Length ] : ref bucket ;
475+ ref Entry entry = ref entries [ index ] ;
462476
463- // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
464- // i.e. EqualityComparer<string>.Default.
477+ if ( updateFreeList )
478+ {
479+ _freeList = entry . next ;
480+ }
481+ entry . hashCode = hashCode ;
482+ entry . next = targetBucket ;
483+ entry . key = key ;
484+ entry . value = value ;
485+ targetBucket = index ;
486+ _version ++ ;
465487
466- if ( collisionCount > HashHelpers . HashCollisionThreshold && _comparer is NonRandomizedStringEqualityComparer )
488+ // Value types never rehash
489+ if ( default ( TKey ) == null && collisionCount > HashHelpers . HashCollisionThreshold && _comparer is NonRandomizedStringEqualityComparer )
467490 {
491+ // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
492+ // i.e. EqualityComparer<string>.Default.
468493 _comparer = ( IEqualityComparer < TKey > ) EqualityComparer < string > . Default ;
469- Resize ( _entries . Length , true ) ;
494+ Resize ( entries . Length , true ) ;
470495 }
471496
472497 return true ;
@@ -525,6 +550,8 @@ private void Resize()
525550
526551 private void Resize ( int newSize , bool forceNewHashCodes )
527552 {
553+ // Value types never rehash
554+ Debug . Assert ( ! forceNewHashCodes || default ( TKey ) == null ) ;
528555 Debug . Assert ( newSize >= _entries . Length ) ;
529556
530557 int [ ] buckets = new int [ newSize ] ;
@@ -537,7 +564,7 @@ private void Resize(int newSize, bool forceNewHashCodes)
537564 int count = _count ;
538565 Array . Copy ( _entries , 0 , entries , 0 , count ) ;
539566
540- if ( forceNewHashCodes )
567+ if ( default ( TKey ) == null && forceNewHashCodes )
541568 {
542569 for ( int i = 0 ; i < count ; i ++ )
543570 {
0 commit comments