@@ -135,6 +135,16 @@ public BlitzMap(int length) : this(length, 0.8) { }
135135 /// <param name="loadFactor">The load factor to control resizing behavior.</param>
136136 public BlitzMap ( int length , double loadFactor )
137137 {
138+ if ( loadFactor <= 0.0 || loadFactor >= 1.0 )
139+ {
140+ throw new ArgumentOutOfRangeException ( nameof ( loadFactor ) , "Load factor must be > 0.0 and < 1.0" ) ;
141+ }
142+
143+ if ( loadFactor > 0.9 )
144+ {
145+ loadFactor = 0.9 ;
146+ }
147+
138148 _length = ( int ) BitOperations . RoundUpToPowerOf2 ( ( uint ) length ) ;
139149 _mask = ( uint ) _length - 1 ;
140150 _loadFactor = loadFactor ;
@@ -596,77 +606,70 @@ public bool InsertOrUpdate(TKey key, TValue value)
596606 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
597607 public bool Remove ( TKey key )
598608 {
599- // Generate a hash code for the provided key
600609 var hashcode = _hasher . ComputeHash ( key ) ;
601-
602- // Calculate the primary index using the bitwise AND with the mask
603- // This constrains the index within the array bounds (_mask is typically array length - 1)
604- var index = hashcode & _mask ;
605-
606- // Calculate the secondary signature for collision detection
607- // The signature is the remaining bits of the hash not used by the index
610+ uint index = hashcode & _mask ;
608611 var signature = hashcode & ~ _mask ;
609612
610- // Obtain references to the start of the bucket and entry arrays
611613 ref var bucketBase = ref MemoryMarshal . GetArrayDataReference ( _buckets ) ;
612614 ref var entryBase = ref MemoryMarshal . GetArrayDataReference ( _entries ) ;
613615
614- // Start with the initial bucket determined by the hash index
616+ uint prevIndex = INACTIVE ;
615617 ref Bucket bucket = ref Unsafe . Add ( ref bucketBase , index ) ;
616618
617- // Traverse the linked list of buckets to find the matching key
618619 while ( true )
619620 {
620- // Check if the signature matches the desired key's signature
621- if ( signature == ( bucket . Signature & ~ _mask ) )
621+ if ( bucket . Signature != INACTIVE &&
622+ signature == ( bucket . Signature & ~ _mask ) )
622623 {
623- // Retrieve the entry associated with the current bucket
624- var entryIndex = bucket . Signature & _mask ;
624+ uint entryIndex = bucket . Signature & _mask ;
625625 ref var entry = ref Unsafe . Add ( ref entryBase , entryIndex ) ;
626626
627- // Verify that the key matches using the equality comparer
628627 if ( _eq . Equals ( key , entry . Key ) )
629628 {
630- // Erase the bucket and get the index of the next bucket
631- // Update the last slot by decrementing the total filled count
632- var lastSlot = -- _count ;
633-
634- ref var swap = ref Unsafe . Add ( ref entryBase , lastSlot ) ;
629+ // --- unlink bucket from chain ---
630+ if ( prevIndex == INACTIVE )
631+ {
632+ // removing head
633+ if ( bucket . Next != INACTIVE )
634+ {
635+ bucket = Unsafe . Add ( ref bucketBase , bucket . Next ) ;
636+ }
637+ else
638+ {
639+ bucket . Signature = INACTIVE ;
640+ bucket . Next = INACTIVE ;
641+ }
642+ }
643+ else
644+ {
645+ ref var prev = ref Unsafe . Add ( ref bucketBase , prevIndex ) ;
646+ prev . Next = bucket . Next ;
647+ }
635648
636- // If the current slot is not the last filled slot
649+ // --- compact entries ---
650+ uint lastSlot = -- _count ;
637651 if ( entryIndex != lastSlot )
638652 {
639- // Determine the last bucket to update
640- var lastBucket = ( _lastBucket == INACTIVE || index == _lastBucket )
641- ? FindLastBucket ( ref bucketBase , ref entryBase , lastSlot ) : _lastBucket ;
642-
643- // Move the last pair to the current slot
644- entry = swap ;
653+ ref var moved = ref Unsafe . Add ( ref entryBase , lastSlot ) ;
654+ entry = moved ;
645655
646- // Update the index of the last bucket to point to the new slot
647- Unsafe . Add ( ref bucketBase , lastBucket ) . Signature = entryIndex | ( Unsafe . Add ( ref bucketBase , lastBucket ) . Signature & ~ _mask ) ;
656+ // fix bucket pointing to moved entry
657+ uint movedBucket = FindLastBucket ( ref bucketBase , ref entryBase , lastSlot ) ;
658+ Unsafe . Add ( ref bucketBase , movedBucket ) . Signature =
659+ entryIndex | ( Unsafe . Add ( ref bucketBase , movedBucket ) . Signature & ~ _mask ) ;
648660 }
649661
650- // Clear the last entry, as it has been moved
651- swap = default ;
652-
653- // Mark the end of the list as inactive
662+ Unsafe . Add ( ref entryBase , lastSlot ) = default ;
654663 _lastBucket = INACTIVE ;
655-
656- // Set the erased bucket to inactive
657- _buckets [ index ] . Signature = INACTIVE ;
658- return true ; // Key found, return success
664+ return true ;
659665 }
660666 }
661667
662- // If the next bucket index is inactive, the search is exhausted
663668 if ( bucket . Next == INACTIVE )
664- {
665- return false ; // Key not found, return failure
666- }
669+ return false ;
667670
671+ prevIndex = index ;
668672 index = bucket . Next ;
669- // Move to the next bucket in the chain
670673 bucket = ref Unsafe . Add ( ref bucketBase , index ) ;
671674 }
672675 }
@@ -903,8 +906,17 @@ private uint FindEmptyBucket(ref Bucket bucketBase, uint index, uint cint)
903906 public void Clear ( )
904907 {
905908 Array . Clear ( _entries ) ;
906- _buckets . AsSpan ( ) . Fill ( new Bucket { Signature = INACTIVE , Next = INACTIVE } ) ;
909+
910+ _buckets . AsSpan ( ) . Fill ( new Bucket
911+ {
912+ Signature = INACTIVE ,
913+ Next = INACTIVE
914+ } ) ;
915+
907916 _count = 0 ;
917+ _last = 0 ;
918+ _lastBucket = INACTIVE ;
919+ _numBuckets = ( uint ) _length >> 1 ;
908920 }
909921
910922 /// <summary>
0 commit comments