Files
Performance-Fish/Source/PerformanceFish/Cache/ByInt.cs
T
bbradson be3dae46bb Added stat caching, fixed all the bugs
Added RitualObligationTriggers analyzer category,
Fixed defs from other mods with broken market value breaking fish's storage patches,
Fixed storage caches not updating for quality, rotting and similar changes to stored things,
Fixed storage caches not updating capacity when reinstalling storage,
Fixed prison labor compatibility,
Improved multiplayer compatibility,
Fixed ideology style dominance and altar sharing patches throwing errors instead of defaulting to disabled when not having ideology,
Fixed compatibility with RIMMSqol's world pawn GC patch by disabling that patch,
Fixed rare threading issues throwing errors on startup
2024-03-10 20:05:24 +01:00

365 lines
12 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 JetBrains.Annotations;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace PerformanceFish.Cache;
#pragma warning disable CS9091
[PublicAPI]
public record struct ByInt<T, TResult> where T : notnull where TResult : new()
{
public int Key;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ByInt(T key) => Key = FunctionPointers.IndexGetter<T>.Default(key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByInt(int key) => Key = key;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByInt<T, TResult> other) => Key == other.Key;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => Key;
private static List<FishTable<ByInt<T, TResult>, TResult>> _allCaches = [];
private static object _valueInitializerLock = new();
private static FishTable<ByInt<T, TResult>, TResult> _get = InitializeNew();
[ThreadStatic]
private static FishTable<ByInt<T, TResult>, TResult>? _getThreadStatic;
private static Func<ByInt<T, TResult>, TResult>? _valueInitializer;
public static Func<ByInt<T, TResult>, TResult>? ValueInitializer
{
get
{
lock (_valueInitializerLock)
return _valueInitializer;
}
set
{
lock (_valueInitializerLock)
{
value ??= static _ => Reflection.New<TResult>();
_valueInitializer = value;
_get.ValueInitializer = value;
}
}
}
public static FishTable<ByInt<T, TResult>, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= InitializeNew();
}
public static FishTable<ByInt<T, TResult>, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(int key)
=> ref Get.GetOrAddReference(Unsafe.As<int, ByInt<T, TResult>>(ref key));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(ByInt<T, TResult> key) => ref Get.GetOrAddReference(key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(ref ByInt<T, TResult> key) => ref Get.GetOrAddReference(ref key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(int key) => Get.GetOrAdd(Unsafe.As<int, ByInt<T, TResult>>(ref key));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(ByInt<T, TResult> key) => Get.GetOrAdd(key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(ref ByInt<T, TResult> key) => Get.GetOrAdd(ref key);
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(int key)
=> ref Get.GetReference(Unsafe.As<int, ByInt<T, TResult>>(ref key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T key) => ref Get.GetReference(new(key));
[MethodImpl(MethodImplOptions.NoInlining)]
private static FishTable<ByInt<T, TResult>, TResult> InitializeNew()
{
var newCache = Utility.AddNew(ValueInitializer);
lock (_allCaches)
_allCaches.Add(newCache);
return newCache;
}
public static void Clear()
{
lock (_allCaches)
{
foreach (var cache in _allCaches)
cache.Clear();
}
}
}
[PublicAPI]
public record struct ByInt<T1, T2, TResult>
where T1 : notnull where T2 : notnull where TResult : new()
{
public int First, Second;
private static List<FishTable<ByInt<T1, T2, TResult>, TResult>> _allCaches = [];
private static object _valueInitializerLock = new();
private static FishTable<ByInt<T1, T2, TResult>, TResult> _get = InitializeNew();
[ThreadStatic]
private static FishTable<ByInt<T1, T2, TResult>, TResult>? _getThreadStatic;
private static Func<ByInt<T1, T2, TResult>, TResult>? _valueInitializer;
public static Func<ByInt<T1, T2, TResult>, TResult>? ValueInitializer
{
get
{
lock (_valueInitializerLock)
return _valueInitializer;
}
set
{
lock (_valueInitializerLock)
{
value ??= static _ => Reflection.New<TResult>();
_valueInitializer = value;
_get.ValueInitializer = value;
}
}
}
public static FishTable<ByInt<T1, T2, TResult>, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= InitializeNew();
}
private static ref FishTable<ByInt<T1, T2, TResult>, TResult> GetCacheRef
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
_getThreadStatic ??= Utility.AddNew<ByInt<T1, T2, TResult>, TResult>();
return ref _getThreadStatic!;
}
}
public static FishTable<ByInt<T1, T2, TResult>, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(int first, int second)
=> ref Get.GetOrAddReference(new() { First = first, Second = second });
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TResult GetOrAddReference(in ByInt<T1, T2, TResult> key)
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(int first, int second)
=> Get.GetOrAdd(new() { First = first, Second = second });
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult GetOrAdd(in ByInt<T1, T2, TResult> 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 });
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T1 first, T2 second)
=> ref Get.GetReference(new(first, second));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ByInt(T1 first, T2 second)
{
First = FunctionPointers.IndexGetter<T1>.Default(first);
Second = FunctionPointers.IndexGetter<T2>.Default(second);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByInt(int first, int second)
{
First = first;
Second = second;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByInt<T1, T2, TResult> other)
=> Unsafe.As<ByInt<T1, T2, TResult>, long>(ref this) == Unsafe.As<ByInt<T1, T2, TResult>, long>(ref other);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(First, Second);
[MethodImpl(MethodImplOptions.NoInlining)]
private static FishTable<ByInt<T1, T2, TResult>, TResult> InitializeNew()
{
var newCache = Utility.AddNew(ValueInitializer);
lock (_allCaches)
_allCaches.Add(newCache);
return newCache;
}
public static void Clear()
{
lock (_allCaches)
{
foreach (var cache in _allCaches)
cache.Clear();
}
}
}
[PublicAPI]
public record struct ByInt<T1, T2, T3, TResult>
where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new()
{
public int First, Second, Third;
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult> _get
= Utility.AddNew<ByInt<T1, T2, T3, TResult>, TResult>();
[ThreadStatic]
private static FishTable<ByInt<T1, T2, T3, TResult>, TResult>? _getThreadStatic;
public static FishTable<ByInt<T1, T2, T3, TResult>, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<ByInt<T1, T2, T3, TResult>, TResult>();
}
public static FishTable<ByInt<T1, T2, T3, TResult>, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TResult GetOrAddReference(int first, int second, int third)
{
var key = new ByInt<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 ByInt<T1, T2, T3, TResult> key)
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(int first, int second, int third)
=> ref Get.GetReference(new() { First = first, Second = second, Third = third });
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third)
=> ref Get.GetReference(new(first, second, third));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ByInt(T1 first, T2 second, T3 third)
{
First = FunctionPointers.IndexGetter<T1>.Default(first);
Second = FunctionPointers.IndexGetter<T2>.Default(second);
Third = FunctionPointers.IndexGetter<T3>.Default(third);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByInt<T1, T2, T3, TResult> other)
=> (Unsafe.As<ByInt<T1, T2, T3, TResult>, long>(ref this)
== Unsafe.As<ByInt<T1, T2, T3, TResult>, long>(ref other))
& (Third == other.Third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(First, Second, Third);
}
[PublicAPI]
public record struct ByInt<T1, T2, T3, T4, TResult>
where T1 : notnull where T2 : notnull where T3 : notnull where T4 : notnull
where TResult : new()
{
public int First, Second, Third, Fourth;
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> _get
= Utility.AddNew<ByInt<T1, T2, T3, T4, TResult>, TResult>();
[ThreadStatic]
private static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult>? _getThreadStatic;
public static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<ByInt<T1, T2, T3, T4, TResult>, TResult>();
}
public static FishTable<ByInt<T1, T2, T3, T4, TResult>, TResult> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TResult GetOrAddReference(int first, int second, int third, int fourth)
{
var key = new ByInt<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 ByInt<T1, T2, T3, T4, TResult> key)
=> ref Get.GetOrAddReference(ref Unsafe.AsRef(in key));
[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 });
[MethodImpl(MethodImplOptions.NoInlining)]
public static ref TResult GetExistingReference(T1 first, T2 second, T3 third, T4 fourth)
=> ref Get.GetReference(new(first, second, third, fourth));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ByInt(T1 first, T2 second, T3 third, T4 fourth)
{
First = FunctionPointers.IndexGetter<T1>.Default(first);
Second = FunctionPointers.IndexGetter<T2>.Default(second);
Third = FunctionPointers.IndexGetter<T3>.Default(third);
Fourth = FunctionPointers.IndexGetter<T4>.Default(fourth);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByInt<T1, T2, T3, T4, TResult> other)
=> (Unsafe.As<ByInt<T1, T2, T3, T4, TResult>, long>(ref this)
== Unsafe.As<ByInt<T1, T2, T3, T4, TResult>, long>(ref other))
& (Unsafe.As<int, long>(ref Third) == Unsafe.As<int, long>(ref other.Third));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(First, Second, Third, Fourth);
}
#pragma warning restore CS9091