// 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 // ReSharper disable WithExpressionModifiesAllMembers namespace PerformanceFish.Cache; #pragma warning disable CS9091 [PublicAPI] public record struct ByInt where T : notnull where TResult : new() { public int Key; [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ByInt(T key) => Key = FunctionPointers.IndexGetter.Default(key); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ByInt(int key) => Key = key; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ByInt other) => Key == other.Key; [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Key; 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 ??= InitializeNew(); } public static FishTable, TResult> GetDirectly { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _get; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TResult GetOrAddReference(int key) => ref Get.GetOrAddReference(Unsafe.As>(ref key)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TResult GetOrAddReference(ByInt key) => ref Get.GetOrAddReference(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.NoInlining)] public static ref TResult GetExistingReference(int key) => ref Get.GetReference(Unsafe.As>(ref key)); [MethodImpl(MethodImplOptions.NoInlining)] public static ref TResult GetExistingReference(T key) => ref Get.GetReference(new(key)); [MethodImpl(MethodImplOptions.NoInlining)] private static FishTable, TResult> InitializeNew() => Utility.AddNew(_allCaches, ValueInitializer); public static void Clear() => Utility.Internal.ClearIn(_allCaches); } [PublicAPI] public record struct ByInt where T1 : notnull where T2 : notnull where TResult : new() { public int First, 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 ??= InitializeNew(); } private static ref FishTable, TResult> GetCacheRef { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _getThreadStatic ??= Utility.AddNew, TResult>(); return ref _getThreadStatic!; } } public static FishTable, TResult> GetDirectly { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _get; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TResult GetOrAddReference(int first, int second) => ref Get.GetOrAddReference(default(ByInt) with { First = first, Second = second }); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TResult GetOrAddReference(in ByInt key) => ref Get.GetOrAddReference(ref Unsafe.AsRef(in key)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TResult GetOrAdd(int first, int 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(default(ByInt) with { 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.Default(first); Second = FunctionPointers.IndexGetter.Default(second); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ByInt(int first, int second) { First = first; Second = second; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ByInt other) => Unsafe.As, long>(ref this) == Unsafe.As, long>(ref other); [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 Clear() => Utility.Internal.ClearIn(_allCaches); } [PublicAPI] public record struct ByInt where T1 : notnull where T2 : notnull where T3 : notnull where TResult : new() { public int First, Second, Third; 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 ??= InitializeNew(); } public static FishTable, TResult> GetDirectly { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _get; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ref TResult GetOrAddReference(int first, int second, int third) { var key = default(ByInt) with { First = first, Second = second, Third = third }; return ref Get.GetOrAddReference(ref key); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TResult GetOrAddReference(in ByInt 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(default(ByInt) with { 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.Default(first); Second = FunctionPointers.IndexGetter.Default(second); Third = FunctionPointers.IndexGetter.Default(third); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ByInt other) => (Unsafe.As, long>(ref this) == Unsafe.As, long>(ref other)) & (Third == other.Third); [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] public record struct ByInt where T1 : notnull where T2 : notnull where T3 : notnull where T4 : notnull where TResult : new() { public int First, Second, Third, Fourth; 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 ??= InitializeNew(); } public static FishTable, 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 = default(ByInt) with { First = first, Second = second, Third = third, Fourth = fourth }; return ref Get.GetOrAddReference(ref key); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TResult GetOrAddReference(in ByInt 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(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) => 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.Default(first); Second = FunctionPointers.IndexGetter.Default(second); Third = FunctionPointers.IndexGetter.Default(third); Fourth = FunctionPointers.IndexGetter.Default(fourth); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ByInt other) => (Unsafe.As, long>(ref this) == Unsafe.As, long>(ref other)) & (Unsafe.As(ref Third) == Unsafe.As(ref other.Third)); [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