internal Container Resize(int newSize) { Debug.Assert(IsPowerOfTwo(newSize)); // Reallocate both buckets and entries and rebuild the bucket and entries from scratch. // This serves both to scrub entries with expired keys and to put the new entries in the proper bucket. int[] newBuckets = new int[newSize]; for (int bucketIndex = 0; bucketIndex < newSize; bucketIndex++) { newBuckets[bucketIndex] = -1; } Entry[] newEntries = new Entry[newSize]; int newEntriesIndex = 0; // Migrate existing entries to the new table. for (int entriesIndex = 0; entriesIndex < _entries.Length; entriesIndex++) { int hashCode = _entries[entriesIndex].hashCode; DependentHandle depHnd = _entries[entriesIndex].depHnd; if (hashCode != -1 && depHnd.IsAllocated && depHnd.GetPrimary() != null) { // Entry is used and has not expired. Link it into the appropriate bucket list. // Note that we have to copy the DependentHandle, since the original copy will be freed // by the Container's finalizer. newEntries[newEntriesIndex].hashCode = hashCode; newEntries[newEntriesIndex].depHnd = depHnd.AllocateCopy(); int bucket = hashCode & (newBuckets.Length - 1); newEntries[newEntriesIndex].next = newBuckets[bucket]; newBuckets[bucket] = newEntriesIndex; newEntriesIndex++; } } GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. return(new Container(newBuckets, newEntries, newEntriesIndex)); }
internal Container Resize() { // Start by assuming we won't resize. int newSize = _buckets.Length; // If any expired or removed keys exist, we won't resize. bool hasExpiredEntries = false; int entriesIndex; for (entriesIndex = 0; entriesIndex < _entries.Length; entriesIndex++) { if (_entries[entriesIndex].hashCode == -1) { // the entry was removed hasExpiredEntries = true; break; } if (_entries[entriesIndex].depHnd.IsAllocated && _entries[entriesIndex].depHnd.GetPrimary() == null) { // the entry has expired hasExpiredEntries = true; break; } } if (!hasExpiredEntries) { newSize = System.Collections.HashHelpers.GetPrime(_buckets.Length == 0 ? _initialCapacity + 1 : _buckets.Length * 2); } // Reallocate both buckets and entries and rebuild the bucket and freelists from scratch. // This serves both to scrub entries with expired keys and to put the new entries in the proper bucket. int newFreeList = -1; int[] newBuckets = new int[newSize]; for (int bucketIndex = 0; bucketIndex < newSize; bucketIndex++) { newBuckets[bucketIndex] = -1; } Entry[] newEntries = new Entry[newSize]; // Migrate existing entries to the new table. for (entriesIndex = 0; entriesIndex < _entries.Length; entriesIndex++) { DependentHandle depHnd = _entries[entriesIndex].depHnd; if (_entries[entriesIndex].hashCode != -1 && depHnd.IsAllocated && depHnd.GetPrimary() != null) { // Entry is used and has not expired. Link it into the appropriate bucket list. // Note that we have to copy the DependentHandle, since the original copy will be freed // by the Container's finalizer. int bucket = _entries[entriesIndex].hashCode % newSize; newEntries[entriesIndex].depHnd = depHnd.AllocateCopy(); newEntries[entriesIndex].hashCode = _entries[entriesIndex].hashCode; newEntries[entriesIndex].next = newBuckets[bucket]; newBuckets[bucket] = entriesIndex; } else { // Entry has either expired or was on the freelist to begin with. Either way // insert it on the new freelist. // We do not free the underlying GC handle here, as we may be racing with readers who already saw // the old Container. The GC handle will be free'd in Container's finalizer. newEntries[entriesIndex].depHnd = new DependentHandle(); newEntries[entriesIndex].next = newFreeList; newFreeList = entriesIndex; } } // Add remaining entries to freelist. while (entriesIndex != newEntries.Length) { newEntries[entriesIndex].depHnd = new DependentHandle(); newEntries[entriesIndex].next = newFreeList; newFreeList = entriesIndex; entriesIndex++; } GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles. return(new Container() { _buckets = newBuckets, _entries = newEntries, _freeList = newFreeList }); }