Files

213 lines
5.7 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.Diagnostics.CodeAnalysis;
using System.Linq;
namespace PerformanceFish.Cache;
public class ByIndex<T_out> : IList<T_out?>, ICollection
{
private static ByIndex<T_out> _get = Utility.AddNew<ByIndex<T_out>>();
[ThreadStatic]
private static ByIndex<T_out>? _getThreadStatic;
public static ByIndex<T_out> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<ByIndex<T_out>>();
}
public static ByIndex<T_out> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _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 = Utility.AddNew<ByIndex<T_in, T_out>>();
[ThreadStatic]
private static ByIndex<T_in, T_out>? _getThreadStatic;
public new static ByIndex<T_in, T_out> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<ByIndex<T_in, T_out>>();
}
public new static ByIndex<T_in, T_out> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _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 = Utility.AddNew<ByIndex<T_first, T_second, T_out>>();
[ThreadStatic]
private static ByIndex<T_first, T_second, T_out>? _getThreadStatic;
public new static ByIndex<T_first, T_second, T_out> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _getThreadStatic ??= Utility.AddNew<ByIndex<T_first, T_second, T_out>>();
}
public new static ByIndex<T_first, T_second, T_out> GetDirectly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _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 ??= [];
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!);
}
}