diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..760f99c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +[Oo]bj/ diff --git a/1.5/Assemblies/1Fishery.dll b/1.5/Assemblies/1Fishery.dll index e875424..9ff546f 100644 Binary files a/1.5/Assemblies/1Fishery.dll and b/1.5/Assemblies/1Fishery.dll differ diff --git a/1.5/Assemblies/1Fishery.pdb b/1.5/Assemblies/1Fishery.pdb index d5e6082..ad28fad 100644 Binary files a/1.5/Assemblies/1Fishery.pdb and b/1.5/Assemblies/1Fishery.pdb differ diff --git a/1.5/Assemblies/System.Runtime.CompilerServices.Unsafe.dll b/1.5/Assemblies/System.Runtime.CompilerServices.Unsafe.dll index c5ba4e4..7795684 100644 Binary files a/1.5/Assemblies/System.Runtime.CompilerServices.Unsafe.dll and b/1.5/Assemblies/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/1.6/Assemblies/0PrepatcherAPI.dll b/1.6/Assemblies/0PrepatcherAPI.dll new file mode 100644 index 0000000..d869ece Binary files /dev/null and b/1.6/Assemblies/0PrepatcherAPI.dll differ diff --git a/1.6/Assemblies/1Fishery.dll b/1.6/Assemblies/1Fishery.dll new file mode 100644 index 0000000..871c59e Binary files /dev/null and b/1.6/Assemblies/1Fishery.dll differ diff --git a/1.6/Assemblies/1Fishery.pdb b/1.6/Assemblies/1Fishery.pdb new file mode 100644 index 0000000..165c58d Binary files /dev/null and b/1.6/Assemblies/1Fishery.pdb differ diff --git a/1.6/Assemblies/System.Runtime.CompilerServices.Unsafe.dll b/1.6/Assemblies/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 0000000..7795684 Binary files /dev/null and b/1.6/Assemblies/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/About/About.xml b/About/About.xml index 3f9f221..62aa87f 100644 --- a/About/About.xml +++ b/About/About.xml @@ -7,6 +7,7 @@
  • 1.3
  • 1.4
  • 1.5
  • +
  • 1.6
  • bradson FisheryLib/ModIcon diff --git a/Source/FisheryLib/1.6.csproj b/Source/FisheryLib/1.6.csproj new file mode 100644 index 0000000..39a1346 --- /dev/null +++ b/Source/FisheryLib/1.6.csproj @@ -0,0 +1,19 @@ + + + + ..\..\1.6\Assemblies\ + Copyright (c) 2026 Bradson + + + DEBUG;V1_5;V1_6 + + + V1_5;V1_6 + + + + + + + + diff --git a/Source/FisheryLib/CollectionExtensions.cs b/Source/FisheryLib/CollectionExtensions.cs index 4c8b7ed..458c488 100644 --- a/Source/FisheryLib/CollectionExtensions.cs +++ b/Source/FisheryLib/CollectionExtensions.cs @@ -140,21 +140,54 @@ public static class CollectionExtensions array.FillUsingEnumerable(enumerable, startIndex, count); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int[]? GetBuckets(Dictionary dictionary) +#if V1_6 + => dictionary._buckets; +#else + => dictionary.buckets; +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Dictionary.Entry[]? GetEntries(Dictionary 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(int index, T[] entries) + => (uint)index < (uint)entries.Length; + public static ref TValue GetReference(this Dictionary 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(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(this Dictionary 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 /// ref TValue or Unsafe.NullRef{TValue} public static ref TValue TryGetReferenceUnsafe(this Dictionary 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(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(this Dictionary 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(this Dictionary 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(this Dictionary 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(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(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(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(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(this T[] array, int index) => Array.UnsafeLoad(array, index); + public static T UnsafeLoad(this T[] array, int index) => array[index]; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void UnsafeStore(this T[] array, int index, T value) => Array.UnsafeStore(array, index, value); + public static void UnsafeStore(this T[] array, int index, T value) => array[index] = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TTo UnsafeCast(this TFrom obj) => Array.UnsafeMov(obj); + public static TTo UnsafeCast(this TFrom obj) => Unsafe.As(ref obj); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AddRangeFast(this List 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(this Dictionary dictionary) => dictionary.GetType().ComputeManagedObjectSizeEstimate() - + ((uint)sizeof(Dictionary.Entry) * (uint)(dictionary.entries?.Length ?? 0)) - + (sizeof(int) * (uint)(dictionary.buckets?.Length ?? 0)); + + ((uint)sizeof(Dictionary.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((IEnumerable)enumerable); } -} \ No newline at end of file +} diff --git a/Source/FisheryLib/Directory.Build.Props b/Source/FisheryLib/Directory.Build.Props index 3bfedc8..761c58d 100644 --- a/Source/FisheryLib/Directory.Build.Props +++ b/Source/FisheryLib/Directory.Build.Props @@ -27,7 +27,7 @@ - + @@ -90,4 +90,4 @@ - \ No newline at end of file + diff --git a/Source/FisheryLib/Fishery.sln b/Source/FisheryLib/Fishery.sln index b55e133..4741d1d 100644 --- a/Source/FisheryLib/Fishery.sln +++ b/Source/FisheryLib/Fishery.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.1.32120.378 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.5", "1.5.csproj", "{B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.6", "1.6.csproj", "{87022FD2-7E2D-45E4-BFE2-8E445B46DF08}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.4", "1.4.csproj", "{BE85EB31-71CD-443C-8C93-20E2B7F3F549}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "1.3", "1.3.csproj", "{FC5FB1F4-626D-4895-AE5E-922DFE1AE120}" @@ -21,6 +23,10 @@ Global {B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {B5B38CBB-F8EE-451E-A05B-9ED9B03784D2}.Release|Any CPU.Build.0 = Release|Any CPU + {87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87022FD2-7E2D-45E4-BFE2-8E445B46DF08}.Release|Any CPU.Build.0 = Release|Any CPU {BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Debug|Any CPU.Build.0 = Debug|Any CPU {BE85EB31-71CD-443C-8C93-20E2B7F3F549}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Source/FisheryLib/SingletonFactory.cs b/Source/FisheryLib/SingletonFactory.cs index b30148e..fe4ec12 100644 --- a/Source/FisheryLib/SingletonFactory.cs +++ b/Source/FisheryLib/SingletonFactory.cs @@ -49,7 +49,13 @@ public abstract class SingletonFactory where T_base : notnull } private static T_base GetInternal(Type type) - => _instances.GetOrAdd(type, static type => (T_base)Activator.CreateInstance(type, true)); + { + if (_instances.TryGetValue(type, out var existing)) + return existing; + + var instance = _instances.GetOrAdd(type, static type => (T_base)Activator.CreateInstance(type, true)); + return instance; + } private static readonly ConcurrentDictionary _instances = new(); -} \ No newline at end of file +} diff --git a/Source/FisheryLib/SpanCompatExtensions.cs b/Source/FisheryLib/SpanCompatExtensions.cs new file mode 100644 index 0000000..9093e05 --- /dev/null +++ b/Source/FisheryLib/SpanCompatExtensions.cs @@ -0,0 +1,25 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace FisheryLib; + +internal static class SpanCompatExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref T DangerousGetPinnableReference(this Span span) + { + if (span.Length == 0) + return ref Unsafe.AsRef((void*)1); + + return ref span[0]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ref T DangerousGetPinnableReference(this ReadOnlySpan span) + { + if (span.Length == 0) + return ref Unsafe.AsRef((void*)1); + + return ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)); + } +}