// Copyright (c) 2022 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/. namespace PerformanceFish.Cache; public static class ByReferenceRefreshable where T_first : notnull where T_result : IIsRefreshable { private static Dictionary _get = new(); [ThreadStatic] private static Dictionary? _getThreadStatic; public static Dictionary Get => /*UnityData.IsInMainThread ? _get :*/ _getThreadStatic ??= Utility.AddNew>(); public static Dictionary GetDirectly => _get; static ByReferenceRefreshable() => Utility.All.Add(_get); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T_result GetValue(T_first key) { ref var cache = ref Get.TryGetReferenceUnsafe(key); if (Unsafe.IsNullRef(ref cache)) { Get[key] = Reflection.New(); cache = ref Get.GetReference(key)!; goto RefreshCache; } if (!cache.ShouldRefreshNow) return ref cache!; RefreshCache: cache.ShouldRefreshNow = false; Get[key] = cache.SetNewValue(key); return ref cache!; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryGetValue(T_first key, out T_result value) => Get.TryGetValue(key, out value) && !value.ShouldRefreshNow; public static T_result GetValueAndRefreshNow(T_first key) { var cache = Get.TryGetValue(key); cache ??= Reflection.New(); cache.ShouldRefreshNow = false; Get[key] = cache.SetNewValue(key); return cache; } } public struct ByReferenceRefreshable : IEquatable> where T_first : notnull where T_second : notnull where T_result : IIsRefreshable, T_result> { internal ByReference _innerValue; private static Dictionary, T_result> _get = new(); [ThreadStatic] private static Dictionary, T_result>? _getThreadStatic; public static Dictionary, T_result> Get => /*UnityData.IsInMainThread ? _get :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(); public static Dictionary, T_result> GetDirectly => _get; static ByReferenceRefreshable() => Utility.All.Add(_get); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ref T_result GetValue(ref ByReferenceRefreshable key) { // CS8347 without this crap ref var cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key))); if (Unsafe.IsNullRef(ref cache)) { Get[key] = Reflection.New(); cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!; goto RefreshCache; } if (!cache.ShouldRefreshNow) return ref cache!; RefreshCache: cache.ShouldRefreshNow = false; Get[key] = cache.SetNewValue(key); return ref cache!; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryGetValue(ByReferenceRefreshable key, out T_result value) => Get.TryGetValue(key, out value) && !value.ShouldRefreshNow; public static T_result GetValueAndRefreshNow(ByReferenceRefreshable key) { var cache = Get.TryGetValue(ref key); cache ??= Reflection.New(); cache.ShouldRefreshNow = false; Get[key] = cache.SetNewValue(key); return cache; } public ByReferenceRefreshable(T_first first, T_second second) => _innerValue = new(first, second); public T_first First => _innerValue.First; public T_second Second => _innerValue.Second; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ByReferenceRefreshable other) => _innerValue.Equals(other._innerValue); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => _innerValue.GetHashCode(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) => obj is ByReferenceRefreshable cache && Equals(cache); public static bool operator ==(ByReferenceRefreshable left, ByReferenceRefreshable right) => left.Equals(right); public static bool operator !=(ByReferenceRefreshable left, ByReferenceRefreshable right) => !(left == right); } public struct ByReferenceRefreshable : IEquatable> where T_first : notnull where T_second : notnull where T_third : notnull where T_result : IIsRefreshable, T_result> { private ByReference _innerValue; private static Dictionary, T_result> _get = new(); [ThreadStatic] private static Dictionary, T_result>? _getThreadStatic; public static Dictionary, T_result> Get => /*UnityData.IsInMainThread ? _get :*/ _getThreadStatic ??= Utility.AddNew, T_result>>(); public static Dictionary, T_result> GetDirectly => _get; static ByReferenceRefreshable() => Utility.All.Add(_get); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ref T_result GetValue(ref ByReferenceRefreshable key) { ref var cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key))); if (Unsafe.IsNullRef(ref cache)) { Get[key] = Activator.CreateInstance(); cache = ref Unsafe.AsRef(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!; goto RefreshCache; } if (!cache.ShouldRefreshNow) return ref cache!; RefreshCache: cache.ShouldRefreshNow = false; Get[key] = cache.SetNewValue(key); return ref cache!; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryGetValue(ByReferenceRefreshable key, out T_result value) => Get.TryGetValue(key, out value) && !value.ShouldRefreshNow; public static T_result GetValueAndRefreshNow(ByReferenceRefreshable key) { var cache = Get.TryGetValue(ref key); cache ??= Activator.CreateInstance(); cache.ShouldRefreshNow = false; Get[key] = cache.SetNewValue(key); return cache; } public ByReferenceRefreshable(T_first first, T_second second, T_third third) => _innerValue = new(first, second, third); public T_first First => _innerValue.First; public T_second Second => _innerValue.Second; public T_third Third => _innerValue.Third; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ByReferenceRefreshable other) => _innerValue.Equals(other._innerValue); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => _innerValue.GetHashCode(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) => obj is ByReferenceRefreshable cache && Equals(cache); public static bool operator ==(ByReferenceRefreshable left, ByReferenceRefreshable right) => left.Equals(right); public static bool operator !=(ByReferenceRefreshable left, ByReferenceRefreshable right) => !(left == right); }