Performance Fish
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
// 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/.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerformanceFish.Cache;
|
||||
public class ByIndex<T_out> : IList<T_out?>, ICollection
|
||||
{
|
||||
private static ByIndex<T_out> _get = new();
|
||||
[ThreadStatic]
|
||||
private static ByIndex<T_out>? _getThreadStatic;
|
||||
|
||||
public static ByIndex<T_out> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<ByIndex<T_out>>();
|
||||
|
||||
public static ByIndex<T_out> GetDirectly => _get;
|
||||
|
||||
static ByIndex() => Utility.All.Add(_get);
|
||||
|
||||
protected T_out?[] _items = Array.Empty<T_out>();
|
||||
protected object? _syncRoot;
|
||||
|
||||
public T_out? this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
ResizeIfNecessary(index);
|
||||
return _items[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
ResizeIfNecessary(index);
|
||||
_items[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: MaybeNull]
|
||||
public ref T_out GetReference(int index)
|
||||
{
|
||||
ResizeIfNecessary(index);
|
||||
return ref _items[index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetValue(int index, out T_out? value)
|
||||
{
|
||||
value = this[index];
|
||||
return !value!.Equals<T_out>(default!);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void ResizeIfNecessary(int checkedIndex)
|
||||
{
|
||||
if (checkedIndex >= _items.Length)
|
||||
ResizeFor(checkedIndex);
|
||||
}
|
||||
|
||||
protected void ResizeFor(int index)
|
||||
{
|
||||
Array.Resize(ref _items, Mathf.NextPowerOfTwo(index + 1));
|
||||
ResizeIfNecessary(index); // just in case
|
||||
}
|
||||
|
||||
public int Count => _items.Length;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
object ICollection.SyncRoot
|
||||
=> _syncRoot
|
||||
??= Interlocked.CompareExchange<object>(ref _syncRoot!, new(), null!);
|
||||
|
||||
bool ICollection.IsSynchronized => false;
|
||||
|
||||
[DoesNotReturn]
|
||||
public void Add(T_out? item) => throw new NotSupportedException();
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (_items.Length > 0)
|
||||
Array.Clear(_items, 0, _items.Length);
|
||||
}
|
||||
|
||||
public bool Contains(T_out? item) => Array.IndexOf(_items, item) >= 0;
|
||||
|
||||
public void CopyTo(T_out?[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
|
||||
|
||||
public IEnumerator<T_out?> GetEnumerator() => _items.AsEnumerable().GetEnumerator();
|
||||
|
||||
public int IndexOf(T_out? item) => Array.IndexOf(_items, item);
|
||||
|
||||
public void Insert(int index, T_out? item) => this[index] = item;
|
||||
|
||||
[DoesNotReturn]
|
||||
public bool Remove(T_out? item) => throw new NotSupportedException();
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < Count)
|
||||
_items[index] = default;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator();
|
||||
void ICollection.CopyTo(Array array, int index) => _items.CopyTo(array, index);
|
||||
}
|
||||
|
||||
public class ByIndex<T_in, T_out> : ByIndex<T_out>
|
||||
where T_in : notnull
|
||||
{
|
||||
private static ByIndex<T_in, T_out> _get = new();
|
||||
[ThreadStatic]
|
||||
private static ByIndex<T_in, T_out>? _getThreadStatic;
|
||||
|
||||
public new static ByIndex<T_in, T_out> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<ByIndex<T_in, T_out>>();
|
||||
|
||||
public new static ByIndex<T_in, T_out> GetDirectly => _get;
|
||||
|
||||
static ByIndex() => Utility.All.Add(_get);
|
||||
|
||||
public unsafe T_out? this[T_in key]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this[FunctionPointers.IndexGetter<T_in>.Default(key)];
|
||||
set => this[FunctionPointers.IndexGetter<T_in>.Default(key)] = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetValue(T_in key, [NotNullWhen(true)] out T_out? value)
|
||||
{
|
||||
value = this[key];
|
||||
return !value!.Equals<T_out>(default!);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: MaybeNull]
|
||||
public unsafe ref T_out GetReference(T_in key)
|
||||
=> ref GetReference(FunctionPointers.IndexGetter<T_in>.Default(key))!;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seems slower than the dictionary based Cache.ByInt.
|
||||
/// </summary>
|
||||
public class ByIndex<T_first, T_second, T_out> : ByIndex<T_first, ByIndex<T_second, T_out>>
|
||||
where T_first : notnull where T_second : notnull
|
||||
{
|
||||
private static ByIndex<T_first, T_second, T_out> _get = new();
|
||||
[ThreadStatic]
|
||||
private static ByIndex<T_first, T_second, T_out>? _getThreadStatic;
|
||||
|
||||
public new static ByIndex<T_first, T_second, T_out> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<ByIndex<T_first, T_second, T_out>>();
|
||||
|
||||
public new static ByIndex<T_first, T_second, T_out> GetDirectly => _get;
|
||||
|
||||
static ByIndex() => Utility.All.Add(_get);
|
||||
|
||||
public T_out? this[T_first first, T_second second]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => GetInner(first)[second];
|
||||
set => GetInner(first)[second] = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe ByIndex<T_second, T_out> GetInner(T_first first)
|
||||
{
|
||||
var firstIndex = FunctionPointers.IndexGetter<T_first>.Default(first);
|
||||
ResizeIfNecessary(firstIndex);
|
||||
|
||||
ref var inner = ref _items[firstIndex];
|
||||
inner ??= new();
|
||||
|
||||
return inner!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: MaybeNull]
|
||||
public ref T_out GetReference(T_first first, T_second second)
|
||||
=> ref GetInner(first).GetReference(second)!;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetValue(T_first first, T_second second, [NotNullWhen(true)] out T_out? value)
|
||||
{
|
||||
value = this[first, second];
|
||||
return !value!.Equals<T_out>(default!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
// 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/.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PerformanceFish.Cache;
|
||||
public struct ByInt<T_in, T_result>
|
||||
where T_in : notnull
|
||||
where T_result : IIsRefreshable<T_in, T_result>
|
||||
{
|
||||
private static Dictionary<ByInt<T_in, T_result>, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByInt<T_in, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByInt<T_in, T_result>, T_result> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByInt<T_in, T_result>, T_result>>();
|
||||
|
||||
public static Dictionary<ByInt<T_in, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByInt() => Utility.All.Add(_get);
|
||||
|
||||
public int Key;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe ByInt(T_in key) => Key = FunctionPointers.IndexGetter<T_in>.Default(key);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T_result GetValue(T_in key)
|
||||
{
|
||||
ref var cache = ref Get.TryGetReferenceUnsafe(new(key));
|
||||
|
||||
if (Unsafe.IsNullRef(ref cache))
|
||||
{
|
||||
Get[new(key)] = Reflection.New<T_result>();
|
||||
cache = ref Get.GetReference(new(key))!;
|
||||
goto RefreshCache;
|
||||
}
|
||||
|
||||
if (!cache.ShouldRefreshNow)
|
||||
return ref cache!;
|
||||
|
||||
RefreshCache:
|
||||
cache.ShouldRefreshNow = false;
|
||||
Get[new(key)] = cache.SetNewValue(key);
|
||||
|
||||
return ref cache!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref T_result TryGetReferenceUnsafe(int key)
|
||||
=> ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_in, T_result>>(ref key))));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref T_result TryGetReferenceChecked(int key, out bool result)
|
||||
{
|
||||
ref var value = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_in, T_result>>(ref key))));
|
||||
result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow;
|
||||
return ref value!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetValue(T_in key, [MaybeNullWhen(false)] out T_result value)
|
||||
=> Get.TryGetValue(new(key), out value)
|
||||
&& !value.ShouldRefreshNow;
|
||||
|
||||
public static T_result GetValueAndRefreshNow(T_in key)
|
||||
{
|
||||
var cache = Get.TryGetValue(new(key));
|
||||
cache ??= Reflection.New<T_result>();
|
||||
cache.ShouldRefreshNow = false;
|
||||
Get[new(key)] = cache.SetNewValue(key);
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ByIntRefreshable<T_first, T_second, T_result> : IEquatable<ByIntRefreshable<T_first, T_second, T_result>>
|
||||
where T_first : notnull where T_second : notnull
|
||||
where T_result : IIsRefreshable<ByIntRefreshable<T_first, T_second, T_result>, T_result>
|
||||
{
|
||||
private static EqualityComparer<ByIntRefreshable<T_first, T_second, T_result>> _comparer = new Comparer();
|
||||
private class Comparer : EqualityComparer<ByIntRefreshable<T_first, T_second, T_result>>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(ByIntRefreshable<T_first, T_second, T_result> x, ByIntRefreshable<T_first, T_second, T_result> y)
|
||||
=> x.First == y.First
|
||||
&& x.Second == y.Second;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode(ByIntRefreshable<T_first, T_second, T_result> obj)
|
||||
=> HashCode.Combine(obj.First, obj.Second);
|
||||
}
|
||||
|
||||
private static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result> _get = new(_comparer);
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result>>(_comparer);
|
||||
|
||||
public static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByIntRefreshable() => Utility.All.Add(_get);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref T_result GetValue(ref ByIntRefreshable<T_first, T_second, T_result> key)
|
||||
{
|
||||
// CS8347 without this crap
|
||||
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
|
||||
|
||||
if (Unsafe.IsNullRef(ref cache))
|
||||
{
|
||||
Get[key] = Reflection.New<T_result>();
|
||||
cache = ref Unsafe.AsRef<T_result>(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.NoInlining)]
|
||||
public static unsafe ref T_result TryGetReferenceUnsafe(int second, int first)
|
||||
=> ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByIntRefreshable<T_first, T_second, T_result>>(ref first))));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref T_result TryGetReferenceChecked(int second, int first, out bool result)
|
||||
{
|
||||
ref var value = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByIntRefreshable<T_first, T_second, T_result>>(ref first))));
|
||||
result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow;
|
||||
return ref value!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetValue(ByIntRefreshable<T_first, T_second, T_result> key, [MaybeNullWhen(false)] out T_result value)
|
||||
=> Get.TryGetValue(key, out value)
|
||||
&& !value.ShouldRefreshNow;
|
||||
|
||||
public static T_result GetValueAndRefreshNow(ByIntRefreshable<T_first, T_second, T_result> key)
|
||||
{
|
||||
var cache = Get.TryGetValue(ref key);
|
||||
cache ??= Reflection.New<T_result>();
|
||||
cache.ShouldRefreshNow = false;
|
||||
Get[key] = cache.SetNewValue(key);
|
||||
return cache;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe ByIntRefreshable(T_first first, T_second second)
|
||||
{
|
||||
First = FunctionPointers.IndexGetter<T_first>.Default(first);
|
||||
Second = FunctionPointers.IndexGetter<T_second>.Default(second);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ByIntRefreshable(int first, int second)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
|
||||
public int First;
|
||||
public int Second;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByIntRefreshable<T_first, T_second, T_result> other)
|
||||
=> First == other.First
|
||||
&& Second == other.Second;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine(First, Second);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ByIntRefreshable<T_first, T_second, T_result> cache
|
||||
&& Equals(cache);
|
||||
|
||||
public static bool operator ==(ByIntRefreshable<T_first, T_second, T_result> left, ByIntRefreshable<T_first, T_second, T_result> right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(ByIntRefreshable<T_first, T_second, T_result> left, ByIntRefreshable<T_first, T_second, T_result> right)
|
||||
=> !(left == right);
|
||||
}
|
||||
|
||||
public struct ByInt<T_first, T_second, T_third, T_result> : IEquatable<ByInt<T_first, T_second, T_third, T_result>>
|
||||
where T_first : notnull where T_second : notnull where T_third : notnull
|
||||
where T_result : IIsRefreshable<ByInt<T_first, T_second, T_third, T_result>, T_result>
|
||||
{
|
||||
private static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result>>();
|
||||
|
||||
public static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByInt() => Utility.All.Add(_get);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref T_result GetValue(ByInt<T_first, T_second, T_third, T_result> key)
|
||||
{
|
||||
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
|
||||
|
||||
if (Unsafe.IsNullRef(ref cache))
|
||||
{
|
||||
Get[key] = Reflection.New<T_result>();
|
||||
cache = ref Unsafe.AsRef<T_result>(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.NoInlining)]
|
||||
public static unsafe ref T_result TryGetReferenceUnsafe(int second, int first, int third)
|
||||
=> ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_first, T_second, T_third, T_result>>(ref first))));
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static unsafe ref T_result TryGetReferenceChecked(int second, int first, out bool result)
|
||||
{
|
||||
ref var value = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_first, T_second, T_third, T_result>>(ref first))));
|
||||
result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow;
|
||||
return ref value!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetValue(ByInt<T_first, T_second, T_third, T_result> key, out T_result value)
|
||||
=> Get.TryGetValue(key, out value)
|
||||
&& !value.ShouldRefreshNow;
|
||||
|
||||
public static T_result GetValueAndRefreshNow(ByInt<T_first, T_second, T_third, T_result> key)
|
||||
{
|
||||
var cache = Get.TryGetValue(ref key);
|
||||
cache ??= Reflection.New<T_result>();
|
||||
cache.ShouldRefreshNow = false;
|
||||
Get[key] = cache.SetNewValue(key);
|
||||
return cache;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe ByInt(T_first first, T_second second, T_third third)
|
||||
{
|
||||
First = FunctionPointers.IndexGetter<T_first>.Default(first);
|
||||
Second = FunctionPointers.IndexGetter<T_second>.Default(second);
|
||||
Third = FunctionPointers.IndexGetter<T_third>.Default(third);
|
||||
}
|
||||
|
||||
public int First;
|
||||
public int Second;
|
||||
public int Third;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByInt<T_first, T_second, T_third, T_result> other)
|
||||
=> First == other.First
|
||||
&& Second == other.Second
|
||||
&& Third == other.Third;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine(First, Second, Third);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ByInt<T_first, T_second, T_third, T_result> cache
|
||||
&& Equals(cache);
|
||||
|
||||
public static bool operator ==(ByInt<T_first, T_second, T_third, T_result> left, ByInt<T_first, T_second, T_third, T_result> right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(ByInt<T_first, T_second, T_third, T_result> left, ByInt<T_first, T_second, T_third, T_result> right)
|
||||
=> !(left == right);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 ByMap<T> where T : new()
|
||||
{
|
||||
private static List<(Map map, T value)> _cache = new();
|
||||
[ThreadStatic]
|
||||
private static List<(Map map, T value)>? _cacheThreadStatic;
|
||||
|
||||
public static List<(Map map, T value)> GetCache
|
||||
=> UnityData.IsInMainThread ? _cache
|
||||
: _cacheThreadStatic ??= Utility.AddNew<List<(Map map, T value)>>();
|
||||
|
||||
public static List<(Map map, T value)> GetCacheDirectly => _cache;
|
||||
|
||||
static ByMap() => Utility.All.Add(_cache);
|
||||
|
||||
private static Func<Map, T> Initialize { get; }
|
||||
= typeof(MapComponent).IsAssignableFrom(typeof(T))
|
||||
? AccessTools.MethodDelegate<Func<Map, T>>(AccessTools.Method(typeof(Map), nameof(Map.GetComponent), Array.Empty<Type>(), new[] { typeof(T) }))
|
||||
// missing T constraints cause the compiler to complain when trying to just use map => map.GetComponent<T>
|
||||
: _ => new();
|
||||
|
||||
public static T GetFor(Map map)
|
||||
{
|
||||
var cache = GetCache;
|
||||
for (var i = 0; i < cache.Count; i++)
|
||||
{
|
||||
var cacheEntry = cache[i];
|
||||
if (cacheEntry.map == map)
|
||||
return cacheEntry.value;
|
||||
|
||||
if (Find.Maps.Count <= i + 1
|
||||
&& !Find.Maps.Contains(cacheEntry.map))
|
||||
{
|
||||
cache.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
cache.Add((map, Initialize(map)));
|
||||
return cache[^1].value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
// 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 ByReference<T_first, T_result>
|
||||
where T_first : notnull
|
||||
{
|
||||
private static Dictionary<T_first, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<T_first, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<T_first, T_result> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<T_first, T_result>>();
|
||||
}
|
||||
|
||||
public static Dictionary<T_first, T_result> GetDirectly => _get;
|
||||
|
||||
static ByReference() => Utility.All.Add(_get);
|
||||
|
||||
public static unsafe void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_first>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_result>).FullDescription()}\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ByReference<T_first, T_second, T_result> : IEquatable<ByReference<T_first, T_second, T_result>>
|
||||
where T_first : notnull where T_second : notnull
|
||||
{
|
||||
private static Dictionary<ByReference<T_first, T_second, T_result>, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByReference<T_first, T_second, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByReference<T_first, T_second, T_result>, T_result> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T_first, T_second, T_result>, T_result>>();
|
||||
}
|
||||
|
||||
public static Dictionary<ByReference<T_first, T_second, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByReference() => Utility.All.Add(_get);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ByReference(T_first first, T_second second)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
|
||||
public static unsafe void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_first>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_second>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T_first, T_second, T_result>>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_second>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T_first, T_second, T_result>>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_result>).FullDescription()}\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public T_first First;
|
||||
|
||||
public T_second Second;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByReference<T_first, T_second, T_result> other)
|
||||
=> First.Equals<T_first>(other.First)
|
||||
&& Second.Equals<T_second>(other.Second);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine(First, Second);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ByReference<T_first, T_second, T_result> cache
|
||||
&& Equals(cache);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(ByReference<T_first, T_second, T_result> left, ByReference<T_first, T_second, T_result> right)
|
||||
=> left.Equals(right);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(ByReference<T_first, T_second, T_result> left, ByReference<T_first, T_second, T_result> right)
|
||||
=> !(left == right);
|
||||
}
|
||||
|
||||
public struct ByReference<T_first, T_second, T_third, T_result> : IEquatable<ByReference<T_first, T_second, T_third, T_result>>
|
||||
where T_first : notnull where T_second : notnull where T_third : notnull
|
||||
{
|
||||
private static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
/*UnityData.IsInMainThread ? _get
|
||||
:*/ => _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result>>();
|
||||
}
|
||||
|
||||
public static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByReference() => Utility.All.Add(_get);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ByReference(T_first first, T_second second, T_third third)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
Third = third;
|
||||
_hashCode = HashCode.Combine(first, second, third);
|
||||
}
|
||||
|
||||
public static unsafe void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_first>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_second>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_third>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T_first, T_second, T_third, T_result>>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_second>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_third>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T_first, T_second, T_third, T_result>>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_second, T_result>).FullDescription()}\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public T_first First;
|
||||
|
||||
public T_second Second;
|
||||
|
||||
public T_third Third;
|
||||
|
||||
private int _hashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByReference<T_first, T_second, T_third, T_result> other)
|
||||
=> First.Equals<T_first>(other.First)
|
||||
&& Second.Equals<T_second>(other.Second)
|
||||
&& Third.Equals<T_third>(other.Third);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => _hashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ByReference<T_first, T_second, T_third, T_result> cache
|
||||
&& Equals(cache);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(ByReference<T_first, T_second, T_third, T_result> left, ByReference<T_first, T_second, T_third, T_result> right)
|
||||
=> left.Equals(right);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(ByReference<T_first, T_second, T_third, T_result> left, ByReference<T_first, T_second, T_third, T_result> right)
|
||||
=> !(left == right);
|
||||
}
|
||||
|
||||
public struct ByReference<T_first, T_second, T_third, T_fourth, T_result> : IEquatable<ByReference<T_first, T_second, T_third, T_fourth, T_result>>
|
||||
where T_first : notnull where T_second : notnull where T_third : notnull where T_fourth : notnull
|
||||
{
|
||||
private static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result> Get
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
/*UnityData.IsInMainThread ? _get
|
||||
:*/ => _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result>>();
|
||||
}
|
||||
|
||||
public static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByReference() => Utility.All.Add(_get);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ByReference(T_first first, T_second second, T_third third, T_fourth 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<T_first>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_second>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_third>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<T_fourth>.Default;
|
||||
_ = FisheryLib.FunctionPointers.Equals<ByReference<T_first, T_second, T_third, T_fourth, T_result>>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_second>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_third>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<T_fourth>.Default;
|
||||
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T_first, T_second, T_third, T_fourth, T_result>>.Default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_second, T_third, T_result>).FullDescription()}\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public T_first First;
|
||||
|
||||
public T_second Second;
|
||||
|
||||
public T_third Third;
|
||||
|
||||
public T_fourth Fourth;
|
||||
|
||||
private int _hashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ByReference<T_first, T_second, T_third, T_fourth, T_result> other)
|
||||
=> First.Equals<T_first>(other.First)
|
||||
&& Second.Equals<T_second>(other.Second)
|
||||
&& Third.Equals<T_third>(other.Third)
|
||||
&& Fourth.Equals<T_fourth>(other.Fourth);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => _hashCode;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ByReference<T_first, T_second, T_third, T_fourth, T_result> cache
|
||||
&& Equals(cache);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(ByReference<T_first, T_second, T_third, T_fourth, T_result> left, ByReference<T_first, T_second, T_third, T_fourth, T_result> right)
|
||||
=> left.Equals(right);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(ByReference<T_first, T_second, T_third, T_fourth, T_result> left, ByReference<T_first, T_second, T_third, T_fourth, T_result> right)
|
||||
=> !(left == right);
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
// 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<T_first, T_result>
|
||||
where T_first : notnull
|
||||
where T_result : IIsRefreshable<T_first, T_result>
|
||||
{
|
||||
private static Dictionary<T_first, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<T_first, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<T_first, T_result> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<T_first, T_result>>();
|
||||
|
||||
public static Dictionary<T_first, T_result> 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<T_result>();
|
||||
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<T_result>();
|
||||
cache.ShouldRefreshNow = false;
|
||||
Get[key] = cache.SetNewValue(key);
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ByReferenceRefreshable<T_first, T_second, T_result> : IEquatable<ByReferenceRefreshable<T_first, T_second, T_result>>
|
||||
where T_first : notnull where T_second : notnull
|
||||
where T_result : IIsRefreshable<ByReferenceRefreshable<T_first, T_second, T_result>, T_result>
|
||||
{
|
||||
internal ByReference<T_first, T_second, T_result> _innerValue;
|
||||
|
||||
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result>>();
|
||||
|
||||
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByReferenceRefreshable() => Utility.All.Add(_get);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref T_result GetValue(ref ByReferenceRefreshable<T_first, T_second, T_result> key)
|
||||
{
|
||||
// CS8347 without this crap
|
||||
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
|
||||
|
||||
if (Unsafe.IsNullRef(ref cache))
|
||||
{
|
||||
Get[key] = Reflection.New<T_result>();
|
||||
cache = ref Unsafe.AsRef<T_result>(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<T_first, T_second, T_result> key, out T_result value)
|
||||
=> Get.TryGetValue(key, out value)
|
||||
&& !value.ShouldRefreshNow;
|
||||
|
||||
public static T_result GetValueAndRefreshNow(ByReferenceRefreshable<T_first, T_second, T_result> key)
|
||||
{
|
||||
var cache = Get.TryGetValue(ref key);
|
||||
cache ??= Reflection.New<T_result>();
|
||||
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<T_first, T_second, T_result> 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<T_first, T_second, T_result> cache
|
||||
&& Equals(cache);
|
||||
|
||||
public static bool operator ==(ByReferenceRefreshable<T_first, T_second, T_result> left, ByReferenceRefreshable<T_first, T_second, T_result> right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(ByReferenceRefreshable<T_first, T_second, T_result> left, ByReferenceRefreshable<T_first, T_second, T_result> right)
|
||||
=> !(left == right);
|
||||
}
|
||||
|
||||
public struct ByReferenceRefreshable<T_first, T_second, T_third, T_result> : IEquatable<ByReferenceRefreshable<T_first, T_second, T_third, T_result>>
|
||||
where T_first : notnull where T_second : notnull where T_third : notnull
|
||||
where T_result : IIsRefreshable<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result>
|
||||
{
|
||||
private ByReference<T_first, T_second, T_third, T_result> _innerValue;
|
||||
|
||||
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result> _get = new();
|
||||
[ThreadStatic]
|
||||
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result>? _getThreadStatic;
|
||||
|
||||
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result> Get
|
||||
=> /*UnityData.IsInMainThread ? _get
|
||||
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result>>();
|
||||
|
||||
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result> GetDirectly => _get;
|
||||
|
||||
static ByReferenceRefreshable() => Utility.All.Add(_get);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref T_result GetValue(ref ByReferenceRefreshable<T_first, T_second, T_third, T_result> key)
|
||||
{
|
||||
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
|
||||
|
||||
if (Unsafe.IsNullRef(ref cache))
|
||||
{
|
||||
Get[key] = Activator.CreateInstance<T_result>();
|
||||
cache = ref Unsafe.AsRef<T_result>(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<T_first, T_second, T_third, T_result> key, out T_result value)
|
||||
=> Get.TryGetValue(key, out value)
|
||||
&& !value.ShouldRefreshNow;
|
||||
|
||||
public static T_result GetValueAndRefreshNow(ByReferenceRefreshable<T_first, T_second, T_third, T_result> key)
|
||||
{
|
||||
var cache = Get.TryGetValue(ref key);
|
||||
cache ??= Activator.CreateInstance<T_result>();
|
||||
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<T_first, T_second, T_third, T_result> 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<T_first, T_second, T_third, T_result> cache
|
||||
&& Equals(cache);
|
||||
|
||||
public static bool operator ==(ByReferenceRefreshable<T_first, T_second, T_third, T_result> left, ByReferenceRefreshable<T_first, T_second, T_third, T_result> right)
|
||||
=> left.Equals(right);
|
||||
|
||||
public static bool operator !=(ByReferenceRefreshable<T_first, T_second, T_third, T_result> left, ByReferenceRefreshable<T_first, T_second, T_third, T_result> right)
|
||||
=> !(left == right);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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 interface IIsRefreshable<T, V> where V : IIsRefreshable<T, V>
|
||||
{
|
||||
public bool ShouldRefreshNow
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public V SetNewValue(T key);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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/.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
|
||||
namespace PerformanceFish.Cache;
|
||||
public static class Utility
|
||||
{
|
||||
public static ConcurrentBag<ICollection> All { get; } = new();
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
foreach (var cache in All)
|
||||
{
|
||||
cache.GetType()
|
||||
.GetMethods()
|
||||
.Where(m
|
||||
=> m.Name == nameof(Array.Clear)
|
||||
&& m.GetParameters().Length == 0)
|
||||
.First()
|
||||
.Invoke(cache, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static T AddNew<T>() where T : ICollection, new()
|
||||
{
|
||||
var newCollection = new T();
|
||||
All.Add(newCollection);
|
||||
return newCollection;
|
||||
}
|
||||
|
||||
public static T AddNew<T>(IEqualityComparer comparer) where T : ICollection, new()
|
||||
{
|
||||
var newCollection = (T)Activator.CreateInstance(typeof(T), comparer);
|
||||
All.Add(newCollection);
|
||||
return newCollection;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate int IndexGetter<T>(T item);
|
||||
Reference in New Issue
Block a user