diff --git a/1.4/Assemblies/PerformanceFish.dll b/1.4/Assemblies/PerformanceFish.dll
index ef9e5bc..195b13e 100644
Binary files a/1.4/Assemblies/PerformanceFish.dll and b/1.4/Assemblies/PerformanceFish.dll differ
diff --git a/1.4/Assemblies/PerformanceFish.pdb b/1.4/Assemblies/PerformanceFish.pdb
index 6f1bd9b..7efb133 100644
Binary files a/1.4/Assemblies/PerformanceFish.pdb and b/1.4/Assemblies/PerformanceFish.pdb differ
diff --git a/1.4/Defs/GasDefs.xml b/1.4/Defs/GasDefs.xml
deleted file mode 100644
index 6d2ec84..0000000
--- a/1.4/Defs/GasDefs.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
- BlindSmoke
-
- 4
- false
- (200,200,200)
-
-
-
- ToxGas
-
- 3
- true
- (180,214,24)
-
-
-
- RotStink
-
- 4
- true
- (214,90,24)
-
-
-
- FirstTestGas
-
- 2
- false
- (229,52,235)
-
-
-
- SecondTestGas
-
- 1
- true
- (52,113,235)
-
-
\ No newline at end of file
diff --git a/1.5/Assemblies/PerformanceFish.dll b/1.5/Assemblies/PerformanceFish.dll
new file mode 100644
index 0000000..52459c0
Binary files /dev/null and b/1.5/Assemblies/PerformanceFish.dll differ
diff --git a/1.5/Assemblies/PerformanceFish.pdb b/1.5/Assemblies/PerformanceFish.pdb
new file mode 100644
index 0000000..c1b3851
Binary files /dev/null and b/1.5/Assemblies/PerformanceFish.pdb differ
diff --git a/About/About.xml b/About/About.xml
index 9e3e869..331c3af 100644
--- a/About/About.xml
+++ b/About/About.xml
@@ -4,8 +4,10 @@
bs.performance
1.4
+ 1.5
bradson
+ PerformanceFish/ModIcon
https://github.com/bbradson/performance-fish
Dubwise.DubsPerformanceAnalyzer
diff --git a/About/ModIcon.dds b/About/ModIcon.dds
new file mode 100644
index 0000000..9c29334
Binary files /dev/null and b/About/ModIcon.dds differ
diff --git a/About/ModIcon.png b/About/ModIcon.png
new file mode 100644
index 0000000..ed68faf
Binary files /dev/null and b/About/ModIcon.png differ
diff --git a/Defs/GasDefs.xml b/Defs/GasDefs.xml
new file mode 100644
index 0000000..58b3de0
--- /dev/null
+++ b/Defs/GasDefs.xml
@@ -0,0 +1,314 @@
+
+
+
+ BlindSmoke
+
+ 4
+ false
+ (200,200,200)
+
+
+
+ ToxGas
+
+ 3
+ true
+ (180,214,24)
+
+
+
+ RotStink
+
+ 4
+ true
+ (214,90,24)
+
+
+
+ DeadLifeDust
+
+ 3
+ true
+ (3,13,51)
+
+
+
+ FirstTestGas
+
+ 2
+ false
+ (229,52,235)
+
+
+
+ SecondTestGas
+
+ 1
+ true
+ (52,113,235)
+
+
+
+ ThirdTestGas
+
+ 1
+ true
+ (255,113,0,0)
+
+
+
+
\ No newline at end of file
diff --git a/1.4/Defs/MainButtonDefs.xml b/Defs/MainButtonDefs.xml
similarity index 100%
rename from 1.4/Defs/MainButtonDefs.xml
rename to Defs/MainButtonDefs.xml
diff --git a/DubsAnalyzer.xml b/DubsAnalyzer.xml
index ac34f3e..8a6fa9d 100644
--- a/DubsAnalyzer.xml
+++ b/DubsAnalyzer.xml
@@ -184,6 +184,14 @@
+
+ TransformValue methods of StatParts. These run on every stat lookup, when not cached
+ Tick
+
+ RimWorld.StatPart:TransformValue
+
+
+
Tick methods of comps attached to Things. Any ThingWithComps can have these, and any mod can add new ones
Tick
diff --git a/Source/PerformanceFish/1.4.csproj b/Source/PerformanceFish/1.4.csproj
new file mode 100644
index 0000000..2461f23
--- /dev/null
+++ b/Source/PerformanceFish/1.4.csproj
@@ -0,0 +1,21 @@
+
+
+
+ ..\..\1.4\Assemblies\
+ Copyright (c) 2023 Bradson
+
+
+ DEBUG;V1_4
+
+
+ V1_4
+
+
+
+
+
+ ..\..\..\Fishery\1.4\Assemblies\1Fishery.dll
+ False
+
+
+
\ No newline at end of file
diff --git a/Source/PerformanceFish/1.5.csproj b/Source/PerformanceFish/1.5.csproj
new file mode 100644
index 0000000..a12a55d
--- /dev/null
+++ b/Source/PerformanceFish/1.5.csproj
@@ -0,0 +1,21 @@
+
+
+
+ ..\..\1.5\Assemblies\
+ Copyright (c) 2024 Bradson
+
+
+ DEBUG;V1_5
+
+
+ V1_5
+
+
+
+
+
+ ..\..\..\Fishery\1.5\Assemblies\1Fishery.dll
+ False
+
+
+
\ No newline at end of file
diff --git a/Source/PerformanceFish/AnalyzerFixes.cs b/Source/PerformanceFish/AnalyzerFixes.cs
index 02084af..5fb2353 100644
--- a/Source/PerformanceFish/AnalyzerFixes.cs
+++ b/Source/PerformanceFish/AnalyzerFixes.cs
@@ -4,6 +4,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
+using System.IO;
using System.Linq;
using System.Reflection.Emit;
using System.Xml;
@@ -305,9 +306,28 @@ public static class AnalyzerFixes
var methods = Reflection.MethodsOfName(AccessTools.TypeByName(array[0]), array[1]);
foreach (var method in methods)
{
- yield return method.IsGenericMethodDefinition
- ? method.MakeGenericMethod(GetTypeArguments(method))
- : method;
+ if (method.IsGenericMethodDefinition || method.ReturnType.IsByRef)
+ continue;
+
+ MethodInfo? methodToReturn;
+ try
+ {
+ methodToReturn = method.IsGenericMethodDefinition
+ ? method.MakeGenericMethod(GetTypeArguments(method))
+ : method;
+ }
+ catch (Exception e)
+ {
+ methodToReturn = null;
+ Log.Error($"Exception caught while trying to parse generic method '{
+ method.FullDescription()}'{
+ (XmlParser.CurrentModDir is { } currentModDir
+ ? $" in 'DubsAnalyzer.xml' within {currentModDir}"
+ : "")}:\n{e}");
+ }
+
+ if (methodToReturn != null)
+ yield return methodToReturn;
}
}
@@ -317,9 +337,10 @@ public static class AnalyzerFixes
var resultArray = new Type[genericArguments.Length];
for (var i = 0; i < genericArguments.Length; i++)
{
- resultArray[i] = genericArguments[i].GetGenericArguments() is var constraints && constraints.Length != 0
- ? constraints[0]
- : typeof(object);
+ var genericArgument = genericArguments[i];
+ resultArray[i] = genericArgument.GetGenericArguments() is var constraints && constraints.Length != 0
+ ? genericArgument.MakeGenericType(constraints)
+ : genericArgument;
}
return resultArray;
@@ -430,6 +451,8 @@ public static class AnalyzerFixes
//Copied from Wiri's analyzer fork, with above fixes integrated. The steam version lacks this functionality
public static class XmlParser
{
+ public static DirectoryInfo? CurrentModDir;
+
[SuppressMessage("Security", "CA3075")]
public static void CollectXmlData()
{
@@ -439,6 +462,8 @@ public static class AnalyzerFixes
if (xmlFiles.Length == 0)
continue;
+ CurrentModDir = dir;
+
foreach (var file in xmlFiles)
{
var doc = new XmlDocument();
@@ -446,6 +471,8 @@ public static class AnalyzerFixes
Parse(doc);
}
+
+ CurrentModDir = null;
}
}
@@ -516,7 +543,17 @@ public static class AnalyzerFixes
continue;
if (childNode.NodeType != XmlNodeType.Comment)
- methods.AddRange(func(childNode.InnerText));
+ {
+ try
+ {
+ methods.AddRange(func(childNode.InnerText));
+ }
+ catch (Exception e)
+ {
+ Log.Error($"Exception caught while trying to parse DubsAnalyzer.xml at '{
+ childNode.InnerText}' in '{CurrentModDir}':\n{e}");
+ }
+ }
}
}
diff --git a/Source/PerformanceFish/Cache/ByInt.cs b/Source/PerformanceFish/Cache/ByInt.cs
index 9aca693..82e7aef 100644
--- a/Source/PerformanceFish/Cache/ByInt.cs
+++ b/Source/PerformanceFish/Cache/ByInt.cs
@@ -5,6 +5,7 @@
using JetBrains.Annotations;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+// ReSharper disable WithExpressionModifiesAllMembers
namespace PerformanceFish.Cache;
@@ -25,12 +26,12 @@ public record struct ByInt where T : notnull where TResult : new()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => Key;
-
- private static List, TResult>> _allCaches = [];
- private static object _valueInitializerLock = new();
+ private static readonly object _valueInitializerLock = new();
- private static FishTable, TResult> _get = InitializeNew();
+ private static readonly List _allCaches = [];
+
+ private static readonly FishTable, TResult> _get = InitializeNew();
[ThreadStatic]
private static FishTable, TResult>? _getThreadStatic;
@@ -74,18 +75,12 @@ public record struct ByInt where T : notnull where TResult : new()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(ByInt key) => ref Get.GetOrAddReference(key);
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ref TResult GetOrAddReference(ref ByInt key) => ref Get.GetOrAddReference(ref key);
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(int key) => Get.GetOrAdd(Unsafe.As>(ref key));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(ByInt key) => Get.GetOrAdd(key);
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static TResult GetOrAdd(ref ByInt key) => Get.GetOrAdd(ref key);
-
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(int key)
=> ref Get.GetReference(Unsafe.As>(ref key));
@@ -95,23 +90,9 @@ public record struct ByInt where T : notnull where TResult : new()
[MethodImpl(MethodImplOptions.NoInlining)]
private static FishTable, TResult> InitializeNew()
- {
- var newCache = Utility.AddNew(ValueInitializer);
-
- lock (_allCaches)
- _allCaches.Add(newCache);
-
- return newCache;
- }
+ => Utility.AddNew(_allCaches, ValueInitializer);
- public static void Clear()
- {
- lock (_allCaches)
- {
- foreach (var cache in _allCaches)
- cache.Clear();
- }
- }
+ public static void Clear() => Utility.Internal.ClearIn(_allCaches);
}
[PublicAPI]
@@ -120,11 +101,11 @@ public record struct ByInt
{
public int First, Second;
- private static List, TResult>> _allCaches = [];
+ private static readonly object _valueInitializerLock = new();
- private static object _valueInitializerLock = new();
+ private static readonly List _allCaches = [];
- private static FishTable, TResult> _get = InitializeNew();
+ private static readonly FishTable, TResult> _get = InitializeNew();
[ThreadStatic]
private static FishTable, TResult>? _getThreadStatic;
@@ -173,7 +154,7 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(int first, int second)
- => ref Get.GetOrAddReference(new() { First = first, Second = second });
+ => ref Get.GetOrAddReference(default(ByInt) with { First = first, Second = second });
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(in ByInt key)
@@ -181,14 +162,14 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(int first, int second)
- => Get.GetOrAdd(new() { First = first, Second = second });
+ => Get.GetOrAdd(default(ByInt) with { First = first, Second = second });
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(in ByInt key) => Get.GetOrAdd(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(int first, int second)
- => ref Get.GetReference(new() { First = first, Second = second });
+ => ref Get.GetReference(default(ByInt) with { First = first, Second = second });
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T1 first, T2 second)
@@ -217,23 +198,9 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.NoInlining)]
private static FishTable, TResult> InitializeNew()
- {
- var newCache = Utility.AddNew(ValueInitializer);
-
- lock (_allCaches)
- _allCaches.Add(newCache);
-
- return newCache;
- }
+ => Utility.AddNew(_allCaches, ValueInitializer);
- public static void Clear()
- {
- lock (_allCaches)
- {
- foreach (var cache in _allCaches)
- cache.Clear();
- }
- }
+ public static void Clear() => Utility.Internal.ClearIn(_allCaches);
}
[PublicAPI]
@@ -242,16 +209,39 @@ public record struct ByInt
{
public int First, Second, Third;
- private static FishTable, TResult> _get
- = Utility.AddNew, TResult>();
+ private static readonly object _valueInitializerLock = new();
+
+ private static readonly List _allCaches = [];
+
+ private static FishTable, TResult> _get = InitializeNew();
[ThreadStatic]
private static FishTable, TResult>? _getThreadStatic;
+ private static Func, TResult>? _valueInitializer;
+
+ public static Func, TResult>? ValueInitializer
+ {
+ get
+ {
+ lock (_valueInitializerLock)
+ return _valueInitializer;
+ }
+ set
+ {
+ lock (_valueInitializerLock)
+ {
+ value ??= static _ => Reflection.New();
+ _valueInitializer = value;
+ _get.ValueInitializer = value;
+ }
+ }
+ }
+
public static FishTable, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _getThreadStatic ??= Utility.AddNew, TResult>();
+ get => _getThreadStatic ??= InitializeNew();
}
public static FishTable, TResult> GetDirectly
@@ -263,7 +253,7 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TResult GetOrAddReference(int first, int second, int third)
{
- var key = new ByInt { First = first, Second = second, Third = third };
+ var key = default(ByInt) with { First = first, Second = second, Third = third };
return ref Get.GetOrAddReference(ref key);
}
@@ -273,7 +263,10 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(int first, int second, int third)
- => ref Get.GetReference(new() { First = first, Second = second, Third = third });
+ => ref Get.GetReference(default(ByInt) with
+ {
+ First = first, Second = second, Third = third
+ });
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third)
@@ -295,6 +288,12 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(First, Second, Third);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static FishTable, TResult> InitializeNew()
+ => Utility.AddNew(_allCaches, ValueInitializer);
+
+ public static void Clear() => Utility.Internal.ClearIn(_allCaches);
}
[PublicAPI]
@@ -304,16 +303,39 @@ public record struct ByInt
{
public int First, Second, Third, Fourth;
- private static FishTable, TResult> _get
- = Utility.AddNew, TResult>();
+ private static readonly object _valueInitializerLock = new();
+
+ private static readonly List _allCaches = [];
+
+ private static FishTable, TResult> _get = InitializeNew();
[ThreadStatic]
private static FishTable, TResult>? _getThreadStatic;
+ private static Func, TResult>? _valueInitializer;
+
+ public static Func, TResult>? ValueInitializer
+ {
+ get
+ {
+ lock (_valueInitializerLock)
+ return _valueInitializer;
+ }
+ set
+ {
+ lock (_valueInitializerLock)
+ {
+ value ??= static _ => Reflection.New();
+ _valueInitializer = value;
+ _get.ValueInitializer = value;
+ }
+ }
+ }
+
public static FishTable, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _getThreadStatic ??= Utility.AddNew, TResult>();
+ get => _getThreadStatic ??= InitializeNew();
}
public static FishTable, TResult> GetDirectly
@@ -325,7 +347,7 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TResult GetOrAddReference(int first, int second, int third, int fourth)
{
- var key = new ByInt
+ var key = default(ByInt) with
{
First = first, Second = second, Third = third, Fourth = fourth
};
@@ -338,7 +360,10 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(int first, int second, int third, int fourth)
- => ref Get.GetReference(new() { First = first, Second = second, Third = third, Fourth = fourth });
+ => ref Get.GetReference(default(ByInt) with
+ {
+ First = first, Second = second, Third = third, Fourth = fourth
+ });
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth)
@@ -361,5 +386,11 @@ public record struct ByInt
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(First, Second, Third, Fourth);
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static FishTable, TResult> InitializeNew()
+ => Utility.AddNew(_allCaches, ValueInitializer);
+
+ public static void Clear() => Utility.Internal.ClearIn(_allCaches);
}
#pragma warning restore CS9091
\ No newline at end of file
diff --git a/Source/PerformanceFish/Cache/ByReference.cs b/Source/PerformanceFish/Cache/ByReference.cs
index 3a19181..6821916 100644
--- a/Source/PerformanceFish/Cache/ByReference.cs
+++ b/Source/PerformanceFish/Cache/ByReference.cs
@@ -8,6 +8,7 @@ using System.Security;
using System.Threading.Tasks;
using JetBrains.Annotations;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+// ReSharper disable WithExpressionModifiesAllMembers
namespace PerformanceFish.Cache;
@@ -18,9 +19,9 @@ public static class ByReferenceUnclearable
{
public const int MINIMUM_CALL_COUNT_FOR_STARTING_TASK = 8;
- public static object SyncLock = new();
+ public static readonly object SyncLock = new();
- private static FishTable _get = Utility.AddNewUnclearable();
+ private static readonly FishTable _get = Utility.AddNewUnclearable();
[ThreadStatic]
private static FishTable? _getThreadStatic;
@@ -138,7 +139,8 @@ public static class ByReferenceUnclearable
}
[SecuritySafeCritical]
- public static Task? RequestFromCacheAsync(T key, TArgument2 second)
+ public static Task? RequestFromCacheAsync(T key,
+ TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable
{
TCacheValue cacheCopy;
@@ -200,8 +202,8 @@ public static class ByReferenceUnclearable
return !cache.Result.Equals(default);
}
- private static async Task UpdateCacheAsync(T key, TCacheValue cacheCopy,
- TArgument2 second)
+ private static async Task UpdateCacheAsync(T key,
+ TCacheValue cacheCopy, TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable
{
TCacheResult? result = default;
@@ -240,11 +242,13 @@ public static class ByReferenceUnclearable
public static class ByReference
where T : notnull where TResult : new()
{
- private static object _valueInitializerLock = new();
+ private static readonly object _valueInitializerLock = new();
- public static object SyncLock = new();
+ public static readonly object SyncLock = new();
+
+ private static readonly List _allCaches = [];
- private static FishTable _get = InitializeNew();
+ private static readonly FishTable _get = InitializeNew();
[ThreadStatic]
private static FishTable? _getThreadStatic;
@@ -293,15 +297,8 @@ public static class ByReference
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(ref T key) => ref Get.GetOrAddReference(ref key);
- [MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T key) => ref Get.GetReference(key);
- public static void Remove(T key)
- {
- Get.Remove(key);
- GetDirectly.Remove(key);
- }
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Update(T key) where VResult : TResult, ICacheable
=> Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key);
@@ -393,7 +390,8 @@ public static class ByReference
return result ?? ThrowHelper.ThrowInvalidOperationException();
}
- public static Task RequestFromCacheAsync(T key, TArgument2 second)
+ public static Task RequestFromCacheAsync(T key,
+ TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable
{
TCacheValue cacheCopy;
@@ -424,8 +422,8 @@ public static class ByReference
return !cache.Result.Equals(default);
}
- private static async ValueTask UpdateCacheAsync(T key, TCacheValue cacheCopy,
- TArgument2 second)
+ private static async ValueTask UpdateCacheAsync(T key,
+ TCacheValue cacheCopy, TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable
{
TCacheResult? result = default;
@@ -444,23 +442,14 @@ public static class ByReference
return result ?? ThrowHelper.ThrowInvalidOperationException();
}
- public static unsafe void Initialize()
- {
- try
- {
- _ = FisheryLib.FunctionPointers.Equals.Default;
- _ = FisheryLib.FunctionPointers.GetHashCode.Default;
- }
- catch (Exception ex)
- {
- Log.Error(
- $"Performance Fish encountered an exception while trying to initialize {
- typeof(ByReference).FullName}\n{ex}");
- }
- }
+ public static void Initialize() => Utility.Internal.Initialize();
[MethodImpl(MethodImplOptions.NoInlining)]
- private static FishTable InitializeNew() => Utility.AddNew(ValueInitializer);
+ private static FishTable InitializeNew() => Utility.AddNew(_allCaches, ValueInitializer);
+
+ public static void Remove(T key) => Utility.Internal.RemoveIn(_allCaches, key);
+
+ public static void Clear() => Utility.Internal.ClearIn(_allCaches);
}
[PublicAPI]
@@ -468,16 +457,43 @@ public static class ByReference
public record struct ByReference : IInitializable
where T1 : notnull where T2 : notnull where TResult : new()
{
- private static FishTable, TResult> _get
- = Utility.AddNew, TResult>();
+ public T1 First;
+ public T2 Second;
+
+ private static readonly object _valueInitializerLock = new();
+
+ private static readonly List _allCaches = [];
+
+ private static readonly FishTable, TResult> _get
+ = InitializeNew();
[ThreadStatic]
private static FishTable, TResult>? _getThreadStatic;
+ private static Func, TResult>? _valueInitializer;
+
+ public static Func, TResult>? ValueInitializer
+ {
+ get
+ {
+ lock (_valueInitializerLock)
+ return _valueInitializer;
+ }
+ set
+ {
+ lock (_valueInitializerLock)
+ {
+ value ??= static _ => Reflection.New();
+ _valueInitializer = value;
+ _get.ValueInitializer = value;
+ }
+ }
+ }
+
public static FishTable, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _getThreadStatic ??= Utility.AddNew, TResult>();
+ get => _getThreadStatic ??= InitializeNew();
}
public static FishTable, TResult> GetDirectly
@@ -488,7 +504,7 @@ public record struct ByReference : IInitializable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(T1 first, T2 second)
- => ref Get.GetOrAddReference(new() { First = first, Second = second });
+ => ref Get.GetOrAddReference(default(ByReference) with { First = first, Second = second });
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(in ByReference key)
@@ -497,7 +513,7 @@ public record struct ByReference : IInitializable
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref TResult GetExistingReference(T1 first, T2 second)
{
- var key = new ByReference(first, second);
+ var key = default(ByReference) with { First = first, Second = second };
return ref Get.GetReference(ref key);
}
@@ -509,7 +525,7 @@ public record struct ByReference : IInitializable
public static void Update(T1 first, T2 second)
where VResult : TResult, ICacheable>
{
- var key = new ByReference(first, second);
+ var key = default(ByReference) with { First = first, Second = second };
Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key);
}
@@ -517,7 +533,7 @@ public record struct ByReference : IInitializable
public static void Update(T1 key1, T2 key2, V2 second)
where VResult : TResult, ICacheable, V2>
{
- var key = new ByReference(key1, key2);
+ var key = default(ByReference) with { First = key1, Second = key2 };
Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
}
@@ -525,7 +541,7 @@ public record struct ByReference : IInitializable
public static void Update(T1 key1, T2 key2, V2 second, V3 third)
where VResult : TResult, ICacheable, V2, V3>
{
- var key = new ByReference(key1, key2);
+ var key = default(ByReference) with { First = key1, Second = key2 };
Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
}
@@ -533,7 +549,7 @@ public record struct ByReference : IInitializable
public static unsafe ref VResult GetAndCheck(T1 first, T2 second)
where VResult : TResult, ICacheable>
{
- var key = new ByReference(first, second);
+ var key = default(ByReference) with { First = first, Second = second };
ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key);
@@ -545,7 +561,7 @@ public record struct ByReference : IInitializable
public static unsafe ref VResult GetAndCheck(T1 key1, T2 key2, V2 second)
where VResult : TResult, ICacheable, V2>
{
- var key = new ByReference(key1, key2);
+ var key = default(ByReference) with { First = key1, Second = key2 };
ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second);
@@ -557,7 +573,7 @@ public record struct ByReference : IInitializable
public static unsafe ref VResult GetAndCheck(T1 key1, T2 key2, V2 second, V3 third)
where VResult : TResult, ICacheable, V2, V3>
{
- var key = new ByReference(key1, key2);
+ var key = default(ByReference) with { First = key1, Second = key2 };
ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second, third);
@@ -579,29 +595,13 @@ public record struct ByReference : IInitializable
Second = second;
}
- public static unsafe void Initialize()
+ public static void Initialize()
{
- try
- {
- _ = FisheryLib.FunctionPointers.Equals.Default;
- _ = FisheryLib.FunctionPointers.Equals.Default;
- _ = FisheryLib.FunctionPointers.Equals>.Default;
- _ = FisheryLib.FunctionPointers.GetHashCode.Default;
- _ = FisheryLib.FunctionPointers.GetHashCode.Default;
- _ = FisheryLib.FunctionPointers.GetHashCode>.Default;
- }
- catch (Exception ex)
- {
- Log.Error(
- $"Performance Fish encountered an exception while trying to initialize {
- typeof(ByReference).FullName}\n{ex}");
- }
+ Utility.Internal.Initialize();
+ Utility.Internal.Initialize();
+ Utility.Internal.Initialize>();
}
- public T1 First;
-
- public T2 Second;
-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReference other)
=> First.Equals(other.First)
@@ -610,6 +610,14 @@ public record struct ByReference : IInitializable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(First, Second);
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static FishTable, TResult> InitializeNew()
+ => Utility.AddNew(_allCaches, ValueInitializer);
+
+ public static void Remove(ByReference key) => Utility.Internal.RemoveIn(_allCaches, key);
+
+ public static void Clear() => Utility.Internal.ClearIn(_allCaches);
+
T1 IMemberCount.First
{
get => First;
@@ -628,22 +636,49 @@ public record struct ByReference : IInitializable
public record struct ByReference
where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new()
{
- // TODO: Switch to FishTable. This is used by some of the reflection patches and currently crashes when not used
- // with standard dictionaries
+ public T1 First;
+ public T2 Second;
+ public T3 Third;
+ public int HashCode;
- private static Dictionary, TResult> _get
- = Utility.AddNew, TResult>>();
+ // TODO: Fix ReflectionCaching:CustomAttributeCache causing crashes when not used with standard dictionaries
+
+ private static readonly object _valueInitializerLock = new();
+
+ private static readonly List _allCaches = [];
+
+ private static readonly FishTable, TResult> _get = InitializeNew();
[ThreadStatic]
- private static Dictionary, TResult>? _getThreadStatic;
+ private static FishTable, TResult>? _getThreadStatic;
- public static Dictionary, TResult> Get
+ private static Func, TResult>? _valueInitializer;
+
+ public static Func, TResult>? ValueInitializer
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _getThreadStatic ??= Utility.AddNew, TResult>>();
+ get
+ {
+ lock (_valueInitializerLock)
+ return _valueInitializer;
+ }
+ set
+ {
+ lock (_valueInitializerLock)
+ {
+ value ??= static _ => Reflection.New();
+ _valueInitializer = value;
+ _get.ValueInitializer = value;
+ }
+ }
}
- public static Dictionary, TResult> GetDirectly
+ public static FishTable, TResult> Get
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _getThreadStatic ??= InitializeNew();
+ }
+
+ public static FishTable, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
@@ -652,7 +687,7 @@ public record struct ByReference
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third)
{
- var key = new ByReference { First = first, Second = second, Third = third };
+ var key = Create(first, second, third);
return ref Get.GetOrAddReference(ref key);
}
@@ -663,7 +698,7 @@ public record struct ByReference
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third)
{
- var key = new ByReference(first, second, third);
+ var key = Create(first, second, third);
return ref Get.GetReference(ref key);
}
@@ -675,7 +710,7 @@ public record struct ByReference
public static void Update(T1 first, T2 second, T3 third)
where VResult : TResult, ICacheable>
{
- var key = new ByReference(first, second, third);
+ var key = Create(first, second, third);
Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key);
}
@@ -683,7 +718,7 @@ public record struct ByReference
public static void Update(T1 key1, T2 key2, T3 key3, V2 second)
where VResult : TResult, ICacheable, V2>
{
- var key = new ByReference(key1, key2, key3);
+ var key = Create(key1, key2, key3);
Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
}
@@ -691,7 +726,7 @@ public record struct ByReference
public static void Update(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
where VResult : TResult, ICacheable, V2, V3>
{
- var key = new ByReference(key1, key2, key3);
+ var key = Create(key1, key2, key3);
Unsafe.As(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
}
@@ -699,7 +734,7 @@ public record struct ByReference
public static unsafe ref VResult GetAndCheck(T1 first, T2 second, T3 third)
where VResult : TResult, ICacheable>
{
- var key = new ByReference(first, second, third);
+ var key = Create(first, second, third);
ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key);
@@ -711,7 +746,7 @@ public record struct ByReference
public static unsafe ref VResult GetAndCheck(T1 key1, T2 key2, T3 key3, V2 second)
where VResult : TResult, ICacheable, V2>
{
- var key = new ByReference(key1, key2, key3);
+ var key = Create(key1, key2, key3);
ref var cache = ref Unsafe.As(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second);
@@ -723,7 +758,7 @@ public record struct ByReference
public static unsafe ref VResult GetAndCheck(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
where VResult : TResult, ICacheable, V2, V3>
{
- var key = new ByReference(key1, key2, key3);
+ var key = Create(key1, key2, key3);
ref var cache = ref Unsafe.As