| File: System\Linq\Parallel\Utils\HashLookup.cs | Web Access |
| Project: src\runtime\src\libraries\System.Linq.Parallel\src\System.Linq.Parallel.csproj (System.Linq.Parallel) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // HashLookup.cs // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace System.Linq.Parallel { /// <summary> /// A simple hash map data structure, derived from the LINQ set we also use. /// </summary> /// <typeparam name="TKey">The kind of keys contained within.</typeparam> /// <typeparam name="TValue">The kind of values contained within.</typeparam> internal sealed class HashLookup<TKey, TValue> { private int[] buckets; private Slot[] slots; private int count; private ulong fastModMultiplier; private readonly IEqualityComparer<TKey>? comparer; private const int HashCodeMask = 0x7fffffff; internal HashLookup() : this(null) { } internal HashLookup(IEqualityComparer<TKey>? comparer) { this.comparer = comparer; buckets = new int[7]; slots = new Slot[7]; if (IntPtr.Size == 8) { fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)buckets.Length); } } // If value is not in set, add it and return true; otherwise return false internal bool Add(TKey key, TValue value) { return !Find(key, true, false, ref value!); } // Check whether value is in set internal bool TryGetValue(TKey key, [MaybeNullWhen(false), AllowNull] ref TValue value) { return Find(key, false, false, ref value!); } internal TValue this[TKey key] { set { TValue? v = value; Find(key, false, true, ref v); } } private int GetKeyHashCode(TKey key) { return HashCodeMask & (key == null ? 0 : (comparer?.GetHashCode(key) ?? key.GetHashCode())); } private bool AreKeysEqual(TKey key1, TKey key2) { return (comparer == null ? ((key1 == null && key2 == null) || (key1 != null && key1.Equals(key2))) : comparer.Equals(key1, key2)); } private bool Find(TKey key, bool add, bool set, [MaybeNullWhen(false)] ref TValue value) { int hashCode = GetKeyHashCode(key); for (int i = buckets[GetBucketIndex(hashCode)] - 1; i >= 0; i = slots[i].next) { if (slots[i].hashCode == hashCode && AreKeysEqual(slots[i].key, key)) { if (set) { slots[i].value = value; return true; } else { value = slots[i].value; return true; } } } if (add) { if (count == slots.Length) Resize(); int index = count; count++; uint bucket = GetBucketIndex(hashCode); slots[index].hashCode = hashCode; slots[index].key = key; slots[index].value = value; slots[index].next = buckets[bucket] - 1; buckets[bucket] = index + 1; } return false; } private uint GetBucketIndex(int hashCode) { int[] buckets = this.buckets; return IntPtr.Size == 8 ? HashHelpers.FastMod((uint)hashCode, (uint)buckets.Length, fastModMultiplier) : (uint)hashCode % (uint)buckets.Length; } private void Resize() { int newSize = HashHelpers.ExpandPrime(count); int[] newBuckets = new int[newSize]; Slot[] newSlots = new Slot[newSize]; Array.Copy(slots, newSlots, count); buckets = newBuckets; slots = newSlots; if (IntPtr.Size == 8) { fastModMultiplier = HashHelpers.GetFastModMultiplier((uint)newSize); } for (int i = 0; i < count; i++) { uint bucket = GetBucketIndex(newSlots[i].hashCode); newSlots[i].next = newBuckets[bucket] - 1; newBuckets[bucket] = i + 1; } } internal int Count { get { return count; } } internal KeyValuePair<TKey, TValue> this[int index] { get { return new KeyValuePair<TKey, TValue>(slots[index].key, slots[index].value); } } internal struct Slot { internal int hashCode; internal int next; internal TKey key; internal TValue value; } } }