add 1.6 support and restore 1.5 compatibility

This commit is contained in:
localavaster
2026-04-07 03:34:45 -06:00
parent 418aad61f2
commit 5a3f6c1cc8
15 changed files with 197 additions and 57 deletions
+135 -53
View File
@@ -140,21 +140,54 @@ public static class CollectionExtensions
array.FillUsingEnumerable(enumerable, startIndex, count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int[]? GetBuckets<TKey, TValue>(Dictionary<TKey, TValue> dictionary)
#if V1_6
=> dictionary._buckets;
#else
=> dictionary.buckets;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Dictionary<TKey, TValue>.Entry[]? GetEntries<TKey, TValue>(Dictionary<TKey, TValue> dictionary)
#if V1_6
=> dictionary._entries;
#else
=> dictionary.entries;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetBucketEntryIndex(int[] buckets, int hashCode)
#if V1_6
=> buckets[hashCode % buckets.Length] - 1;
#else
=> buckets[hashCode % buckets.Length];
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsValidEntryIndex<T>(int index, T[] entries)
=> (uint)index < (uint)entries.Length;
public static ref TValue GetReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(key) & int.MaxValue;
// HashCode.Get can handle null, & int.MaxValue removes any sign
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -163,18 +196,23 @@ public static class CollectionExtensions
public static ref TValue GetReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(ref key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -190,18 +228,23 @@ public static class CollectionExtensions
/// <returns>ref TValue or Unsafe.NullRef{TValue}</returns>
public static ref TValue TryGetReferenceUnsafe<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -210,18 +253,23 @@ public static class CollectionExtensions
public static ref TValue TryGetReferenceUnsafe<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(ref key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -230,18 +278,23 @@ public static class CollectionExtensions
public static TValue? TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(ref key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -251,18 +304,23 @@ public static class CollectionExtensions
[return: MaybeNull]
public static TValue TryGetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var entries = GetEntries(dictionary)!;
var keyCode = HashCode.Get(key) & int.MaxValue;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -276,16 +334,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -300,16 +363,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(ref key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -352,16 +420,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -376,16 +449,21 @@ public static class CollectionExtensions
var keyCode = HashCode.Get(ref key) & int.MaxValue;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return entry.value;
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -400,12 +478,15 @@ public static class CollectionExtensions
var result = true;
StartOfLookup:
if (dictionary.buckets != null)
var buckets = GetBuckets(dictionary);
if (buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
var entries = GetEntries(dictionary)!;
var bucket = GetBucketEntryIndex(buckets, keyCode);
var collisionCount = 0;
while (IsValidEntryIndex(bucket, entries))
{
ref var entry = ref dictionary.entries[bucket];
ref var entry = ref entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
{
value = entry.value;
@@ -413,6 +494,8 @@ public static class CollectionExtensions
}
bucket = entry.next;
if (collisionCount++ >= entries.Length)
break;
}
}
@@ -427,7 +510,7 @@ public static class CollectionExtensions
Guard.IsNotNull(dictionary);
Guard.IsNotNull(predicate);
var entries = dictionary.entries;
var entries = GetEntries(dictionary);
if (entries is null)
return 0;
@@ -653,13 +736,13 @@ public static class CollectionExtensions
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T UnsafeLoad<T>(this T[] array, int index) => Array.UnsafeLoad(array, index);
public static T UnsafeLoad<T>(this T[] array, int index) => array[index];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsafeStore<T>(this T[] array, int index, T value) => Array.UnsafeStore(array, index, value);
public static void UnsafeStore<T>(this T[] array, int index, T value) => array[index] = value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TTo UnsafeCast<TFrom, TTo>(this TFrom obj) => Array.UnsafeMov<TFrom, TTo>(obj);
public static TTo UnsafeCast<TFrom, TTo>(this TFrom obj) => Unsafe.As<TFrom, TTo>(ref obj);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddRangeFast<T>(this List<T> list, T[] range)
@@ -687,8 +770,7 @@ public static class CollectionExtensions
return;
list.EnsureCapacity(list._size + value.Length);
UnsafeBlockCopy(ref value.m_firstChar,
ref list._items[list._size], value.Length);
value.CopyTo(0, list._items, list._size, value.Length);
list._size += value.Length;
list._version++;
@@ -764,8 +846,8 @@ public static class CollectionExtensions
public static unsafe uint GetSizeEstimate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
=> dictionary.GetType().ComputeManagedObjectSizeEstimate()
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(dictionary.entries?.Length ?? 0))
+ (sizeof(int) * (uint)(dictionary.buckets?.Length ?? 0));
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(GetEntries(dictionary)?.Length ?? 0))
+ (sizeof(int) * (uint)(GetBuckets(dictionary)?.Length ?? 0));
internal static unsafe uint ComputeManagedObjectSizeEstimate(this Type type)
{
@@ -857,4 +939,4 @@ public static class CollectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override IList New(IEnumerable enumerable) => new List<T>((IEnumerable<T>)enumerable);
}
}
}