Files
Performance-Fish/Source/PerformanceFish/Cache/ByReference.cs
T

940 lines
32 KiB
C#

// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.InteropServices;
using System.Security;
using System.Threading.Tasks;
using JetBrains.Annotations;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace PerformanceFish.Cache;
#pragma warning disable CS9091, 9082
[PublicAPI]
public static class ByReferenceUnclearable<T, TResult>
where T : notnull where TResult : new()
{
public const int MINIMUM_CALL_COUNT_FOR_STARTING_TASK = 8;
public static object SyncLock = new();
private static FishTable<T, TResult> _get = Utility.AddNewUnclearable<T, TResult>();
[ThreadStatic]
private static FishTable<T, TResult>? _getThreadStatic;
public static FishTable<T, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNewUnclearable<T, TResult>();
}
public static FishTable<T, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(T key) => ref Get.GetOrAddReference(key);
[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);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Update<VResult>(T key) where VResult : TResult, ICacheable<T>
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Update<VResult, V2>(T key, V2 second) where VResult : TResult, ICacheable<T, V2>
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Update<VResult, V2, V3>(T key, V2 second, V3 third)
where VResult : TResult, ICacheable<T, V2, V3>
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref VResult GetAndCheck<VResult>(T key) where VResult : TResult, ICacheable<T>
{
ref var cache = ref Unsafe.As<TResult, VResult>(ref GetOrAddReference(key));
if (cache.Dirty)
cache.Update(ref key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref VResult GetAndCheck<VResult, V2>(T key, V2 second) where VResult : TResult, ICacheable<T, V2>
{
ref var cache = ref Unsafe.As<TResult, VResult>(ref GetOrAddReference(key));
if (cache.Dirty)
cache.Update(ref key, second);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref VResult GetAndCheck<VResult, V2, V3>(T key, V2 second, V3 third)
where VResult : TResult, ICacheable<T, V2, V3>
{
ref var cache = ref Unsafe.As<TResult, VResult>(ref GetOrAddReference(key));
if (cache.Dirty)
cache.Update(ref key, second, third);
return ref cache!;
}
public static bool UpdateAsyncCache<TCacheValue, TCacheResult>(ref TCacheValue cache, T key)
where TCacheValue : TResult, IAsyncCacheable<T, TCacheResult>
{
if (cache.Task is null)
return SyncWithCentralCache<TCacheValue, TCacheResult>(ref cache, key);
if (cache.Task.IsCompletedSuccessfully)
cache.Result = cache.Task.Result;
return !cache.Result.Equals<TCacheResult>(default);
}
private static bool SyncWithCentralCache<TCacheValue, TCacheResult>(ref TCacheValue cache, T key)
where TCacheValue : TResult, IAsyncCacheable<T, TCacheResult>
{
lock (SyncLock)
{
ref var centralCache = ref Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key));
var cacheCopy = centralCache;
centralCache.Task ??= Task.Run(() => UpdateCacheAsync<TCacheValue, TCacheResult>(key, cacheCopy));
cache.Task = centralCache.Task;
cache.Result = centralCache.Result;
return !cache.Result.Equals<TCacheResult>(default);
}
}
private static TCacheResult UpdateCacheAsync<TCacheValue, TCacheResult>(T key, TCacheValue cacheCopy)
where TCacheValue : TResult, IAsyncCacheable<T, TCacheResult>
{
TCacheResult? result = default;
try
{
result = cacheCopy.MakeResultAsync(key);
lock (SyncLock)
Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key)).Result = result;
}
catch (Exception e)
{
Log.Error($"{e}");
}
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
}
[SecuritySafeCritical]
public static Task<TCacheResult>? RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
{
TCacheValue cacheCopy;
lock (SyncLock)
{
ref var centralCache = ref Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key));
cacheCopy = centralCache;
if (centralCache.Task is null)
{
centralCache.Task = CreateTaskCountdown<TCacheResult>();
cacheCopy.Task = null;
}
// ReSharper disable once SuspiciousTypeConversion.Global
// ReSharper disable once PatternNeverMatches
else if (centralCache.Task is Countdown taskCountdown)
{
Interlocked.Decrement(ref taskCountdown.Value);
if (taskCountdown.Value <= 0)
{
cacheCopy.Task = centralCache.Task
= StartTask<TCacheValue, TArgument2, TCacheResult>(key, second, cacheCopy);
}
else
{
cacheCopy.Task = null;
}
}
else
{
cacheCopy.Task = centralCache.Task;
}
}
return cacheCopy.Task;
}
private static Task<TCacheResult> StartTask<TCacheValue, TArgument2, TCacheResult>(T key, TArgument2 second,
TCacheValue cacheCopy) where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
=> Task.Run(() => UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(key, cacheCopy, second));
[SecuritySafeCritical]
private static Task<TCacheResult> CreateTaskCountdown<TCacheResult>()
=> Unsafe.As<Task<TCacheResult>>(new Countdown(MINIMUM_CALL_COUNT_FOR_STARTING_TASK));
public static bool UpdateAsyncCache<TCacheValue, TArgument2, TCacheResult>(ref TCacheValue cache, T key,
TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
{
cache.Task ??= RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(key, second);
if (cache.Task is null)
return false;
if (cache.Task.IsCompletedSuccessfully)
cache.Result = cache.Task.Result;
return !cache.Result.Equals<TCacheResult>(default);
}
private static async Task<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TCacheValue cacheCopy,
TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
{
TCacheResult? result = default;
try
{
result = await cacheCopy.MakeResultAsync(key, second).ConfigureAwait(false);
lock (SyncLock)
Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key)).Result = result;
}
catch (Exception e)
{
Log.Error($"{e}");
}
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T>.Default;
}
catch (Exception ex)
{
Log.Error(
$"Performance Fish encountered an exception while trying to initialize {
typeof(ByReferenceUnclearable<T, TResult>).FullName}\n{ex}");
}
}
}
[PublicAPI]
public static class ByReference<T, TResult>
where T : notnull where TResult : new()
{
private static object _valueInitializerLock = new();
public static object SyncLock = new();
private static FishTable<T, TResult> _get = InitializeNew();
[ThreadStatic]
private static FishTable<T, TResult>? _getThreadStatic;
private static Func<T, TResult>? _valueInitializer;
public static Func<T, TResult>? ValueInitializer
{
get
{
lock (_valueInitializerLock)
return _valueInitializer;
}
set
{
lock (_valueInitializerLock)
{
value ??= static _ => Reflection.New<TResult>();
_valueInitializer = value;
_get.ValueInitializer = value;
}
}
}
public static FishTable<T, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= InitializeNew();
}
public static FishTable<T, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(T key) => Get.GetOrAdd(key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(ref T key) => Get.GetOrAdd(ref key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(T key) => ref Get.GetOrAddReference(key);
[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<VResult>(T key) where VResult : TResult, ICacheable<T>
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Update<VResult, V2>(T key, V2 second) where VResult : TResult, ICacheable<T, V2>
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Update<VResult, V2, V3>(T key, V2 second, V3 third)
where VResult : TResult, ICacheable<T, V2, V3>
=> Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref VResult GetAndCheck<VResult>(T key) where VResult : TResult, ICacheable<T>
{
ref var cache = ref Unsafe.As<TResult, VResult>(ref GetOrAddReference(key));
if (cache.Dirty)
cache.Update(ref key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref VResult GetAndCheck<VResult, V2>(T key, V2 second) where VResult : TResult, ICacheable<T, V2>
{
ref var cache = ref Unsafe.As<TResult, VResult>(ref GetOrAddReference(key));
if (cache.Dirty)
cache.Update(ref key, second);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref VResult GetAndCheck<VResult, V2, V3>(T key, V2 second, V3 third)
where VResult : TResult, ICacheable<T, V2, V3>
{
ref var cache = ref Unsafe.As<TResult, VResult>(ref GetOrAddReference(key));
if (cache.Dirty)
cache.Update(ref key, second, third);
return ref cache!;
}
public static bool UpdateAsyncCache<TCacheValue, TCacheResult>(ref TCacheValue cache, T key)
where TCacheValue : TResult, IAsyncCacheable<T, TCacheResult>
{
if (cache.Task is null)
return SyncWithCentralCache<TCacheValue, TCacheResult>(ref cache, key);
if (cache.Task.IsCompletedSuccessfully)
cache.Result = cache.Task.Result;
return !cache.Result.Equals<TCacheResult>(default);
}
private static bool SyncWithCentralCache<TCacheValue, TCacheResult>(ref TCacheValue cache, T key)
where TCacheValue : TResult, IAsyncCacheable<T, TCacheResult>
{
lock (SyncLock)
{
ref var centralCache = ref Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key));
var cacheCopy = centralCache;
centralCache.Task ??= Task.Run(() => UpdateCacheAsync<TCacheValue, TCacheResult>(key, cacheCopy));
cache.Task = centralCache.Task;
cache.Result = centralCache.Result;
return !cache.Result.Equals<TCacheResult>(default);
}
}
private static TCacheResult UpdateCacheAsync<TCacheValue, TCacheResult>(T key, TCacheValue cacheCopy)
where TCacheValue : TResult, IAsyncCacheable<T, TCacheResult>
{
TCacheResult? result = default;
try
{
result = cacheCopy.MakeResultAsync(key);
lock (SyncLock)
Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key)).Result = result;
}
catch (Exception e)
{
Log.Error($"{e}");
}
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
}
public static Task<TCacheResult> RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
{
TCacheValue cacheCopy;
lock (SyncLock)
{
ref var centralCache = ref Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key));
cacheCopy = centralCache;
centralCache.Task ??= Task.Run(async ()
=> await UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(key, cacheCopy, second)
.ConfigureAwait(false));
cacheCopy.Task = centralCache.Task;
}
return cacheCopy.Task;
}
public static bool UpdateAsyncCache<TCacheValue, TArgument2, TCacheResult>(ref TCacheValue cache, T key,
TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
{
cache.Task ??= RequestFromCacheAsync<TCacheValue, TArgument2, TCacheResult>(key, second);
if (cache.Task.IsCompletedSuccessfully)
cache.Result = cache.Task.Result;
return !cache.Result.Equals<TCacheResult>(default);
}
private static async ValueTask<TCacheResult> UpdateCacheAsync<TCacheValue, TArgument2, TCacheResult>(T key, TCacheValue cacheCopy,
TArgument2 second)
where TCacheValue : TResult, IAsyncCacheable<T, TArgument2, TCacheResult>
{
TCacheResult? result = default;
try
{
result = await cacheCopy.MakeResultAsync(key, second).ConfigureAwait(false);
lock (SyncLock)
Unsafe.As<TResult, TCacheValue>(ref GetDirectly.GetOrAddReference(key)).Result = result;
}
catch (Exception e)
{
Log.Error($"{e}");
}
return result ?? ThrowHelper.ThrowInvalidOperationException<TCacheResult>();
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T>.Default;
}
catch (Exception ex)
{
Log.Error(
$"Performance Fish encountered an exception while trying to initialize {
typeof(ByReference<T, TResult>).FullName}\n{ex}");
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static FishTable<T, TResult> InitializeNew() => Utility.AddNew(ValueInitializer);
}
[PublicAPI]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public record struct ByReference<T1, T2, TResult> : IInitializable<T1, T2>
where T1 : notnull where T2 : notnull where TResult : new()
{
private static FishTable<ByReference<T1, T2, TResult>, TResult> _get
= Utility.AddNew<ByReference<T1, T2, TResult>, TResult>();
[ThreadStatic]
private static FishTable<ByReference<T1, T2, TResult>, TResult>? _getThreadStatic;
public static FishTable<ByReference<T1, T2, TResult>, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<ByReference<T1, T2, TResult>, TResult>();
}
public static FishTable<ByReference<T1, T2, TResult>, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(T1 first, T2 second)
=> ref Get.GetOrAddReference(new() { First = first, Second = second });
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(in ByReference<T1, T2, TResult> key)
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref TResult GetExistingReference(T1 first, T2 second)
{
var key = new ByReference<T1, T2, TResult>(first, second);
return ref Get.GetReference(ref key);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(in ByReference<T1, T2, TResult> key)
=> ref Get.GetReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult>(T1 first, T2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>>
{
var key = new ByReference<T1, T2, TResult>(first, second);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult, V2>(T1 key1, T2 key2, V2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2>
{
var key = new ByReference<T1, T2, TResult>(key1, key2);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult, V2, V3>(T1 key1, T2 key2, V2 second, V3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2, V3>
{
var key = new ByReference<T1, T2, TResult>(key1, key2);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>>
{
var key = new ByReference<T1, T2, TResult>(first, second);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, V2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2>
{
var key = new ByReference<T1, T2, TResult>(key1, key2);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult, V2, V3>(T1 key1, T2 key2, V2 second, V3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, TResult>, V2, V3>
{
var key = new ByReference<T1, T2, TResult>(key1, key2);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second, third);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once ConvertToPrimaryConstructor
public ByReference(T1 first, T2 second)
{
First = first;
Second = second;
}
public void Initialize(T1 first, T2 second)
{
First = first;
Second = second;
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, TResult>>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T1>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T2>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T1, T2, TResult>>.Default;
}
catch (Exception ex)
{
Log.Error(
$"Performance Fish encountered an exception while trying to initialize {
typeof(ByReference<T1, T2, TResult>).FullName}\n{ex}");
}
}
public T1 First;
public T2 Second;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReference<T1, T2, TResult> other)
=> First.Equals<T1>(other.First)
&& Second.Equals<T2>(other.Second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(First, Second);
T1 IMemberCount<T1>.First
{
get => First;
set => First = value;
}
T2 IMemberCount<T1, T2>.Second
{
get => Second;
set => Second = value;
}
}
[PublicAPI]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public record struct ByReference<T1, T2, T3, TResult>
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
private static Dictionary<ByReference<T1, T2, T3, TResult>, TResult> _get
= Utility.AddNew<Dictionary<ByReference<T1, T2, T3, TResult>, TResult>>();
[ThreadStatic]
private static Dictionary<ByReference<T1, T2, T3, TResult>, TResult>? _getThreadStatic;
public static Dictionary<ByReference<T1, T2, T3, TResult>, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T1, T2, T3, TResult>, TResult>>();
}
public static Dictionary<ByReference<T1, T2, T3, TResult>, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third)
{
var key = new ByReference<T1, T2, T3, TResult> { First = first, Second = second, Third = third };
return ref Get.GetOrAddReference(ref key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(in ByReference<T1, T2, T3, TResult> key)
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third)
{
var key = new ByReference<T1, T2, T3, TResult>(first, second, third);
return ref Get.GetReference(ref key);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(in ByReference<T1, T2, T3, TResult> key)
=> ref Get.GetReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult>(T1 first, T2 second, T3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>>
{
var key = new ByReference<T1, T2, T3, TResult>(first, second, third);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult, V2>(T1 key1, T2 key2, T3 key3, V2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2>
{
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2, V3>
{
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second, T3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>>
{
var key = new ByReference<T1, T2, T3, TResult>(first, second, third);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, T3 key3, V2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2>
{
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, V2 second, V3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, TResult>, V2, V3>
{
var key = new ByReference<T1, T2, T3, TResult>(key1, key2, key3);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second, third);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByReference(T1 first, T2 second, T3 third)
{
First = first;
Second = second;
Third = third;
_hashCode = HashCode.Combine(first, second, third);
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
_ = FisheryLib.FunctionPointers.Equals<T3>.Default;
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, T3, TResult>>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T1>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T2>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T3>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T1, T2, T3, TResult>>.Default;
}
catch (Exception ex)
{
Log.Error($"Performance Fish encountered an exception while trying to initialize {
typeof(ByReference<T1, T2, T3, TResult>).FullName}\n{ex}");
}
}
public T1 First;
public T2 Second;
public T3 Third;
private int _hashCode;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReference<T1, T2, T3, TResult> other)
=> First.Equals<T1>(other.First)
&& Second.Equals<T2>(other.Second)
&& Third.Equals<T3>(other.Third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => _hashCode;
}
[PublicAPI]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public record struct ByReference<T1, T2, T3, T4, TResult>
where T1 : notnull where T2 : notnull where T3 : notnull where T4 : notnull where TResult : new()
{
private static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> _get
= Utility.AddNew<ByReference<T1, T2, T3, T4, TResult>, TResult>();
[ThreadStatic]
private static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult>? _getThreadStatic;
public static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<ByReference<T1, T2, T3, T4, TResult>, TResult>();
}
public static FishTable<ByReference<T1, T2, T3, T4, TResult>, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TResult GetOrAddReference(T1 first, T2 second, T3 third, T4 fourth)
{
var key = new ByReference<T1, T2, T3, T4, TResult> { First = first, Second = second, Third = third, Fourth = fourth};
return ref Get.GetOrAddReference(ref key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(in ByReference<T1, T2, T3, T4, TResult> key)
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth)
{
var key = new ByReference<T1, T2, T3, T4, TResult>(first, second, third, fourth);
return ref Get.GetReference(ref key);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(in ByReference<T1, T2, T3, T4, TResult> key)
=> ref Get.GetReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult>(T1 first, T2 second, T3 third, T4 fourth)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>>
{
var key = new ByReference<T1, T2, T3, T4, TResult>(first, second, third, fourth);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult, V2>(T1 key1, T2 key2, T3 key3, T4 key4, V2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2>
{
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Update<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, T4 key4, V2 second, V3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2, V3>
{
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key)).Update(ref key, second, third);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult>(T1 first, T2 second, T3 third, T4 fourth)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>>
{
var key = new ByReference<T1, T2, T3, T4, TResult>(first, second, third, fourth);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult, V2>(T1 key1, T2 key2, T3 key3, T4 key4, V2 second)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2>
{
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref VResult GetAndCheck<VResult, V2, V3>(T1 key1, T2 key2, T3 key3, T4 key4, V2 second,
V3 third)
where VResult : TResult, ICacheable<ByReference<T1, T2, T3, T4, TResult>, V2, V3>
{
var key = new ByReference<T1, T2, T3, T4, TResult>(key1, key2, key3, key4);
ref var cache = ref Unsafe.As<TResult, VResult>(ref Get.GetOrAddReference(ref key));
if (cache.Dirty)
cache.Update(ref key, second, third);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByReference(T1 first, T2 second, T3 third, T4 fourth)
{
First = first;
Second = second;
Third = third;
Fourth = fourth;
_hashCode = HashCode.Combine(first, second, third, fourth);
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T1>.Default;
_ = FisheryLib.FunctionPointers.Equals<T2>.Default;
_ = FisheryLib.FunctionPointers.Equals<T3>.Default;
_ = FisheryLib.FunctionPointers.Equals<T4>.Default;
_ = FisheryLib.FunctionPointers.Equals<ByReference<T1, T2, T3, T4, TResult>>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T1>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T2>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T3>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T4>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T1, T2, T3, T4, TResult>>.Default;
}
catch (Exception ex)
{
Log.Error(
$"Performance Fish encountered an exception while trying to initialize {
typeof(ByReference<T1, T2, T3, T4, TResult>).FullName}\n{ex}");
}
}
public T1 First;
public T2 Second;
public T3 Third;
public T4 Fourth;
private int _hashCode;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReference<T1, T2, T3, T4, TResult> other)
=> First.Equals<T1>(other.First)
&& Second.Equals<T2>(other.Second)
&& Third.Equals<T3>(other.Third)
&& Fourth.Equals<T4>(other.Fourth);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => _hashCode;
}
#pragma warning restore CS9091