using System;
namespace Robust.Shared.Collections;
///
/// Alternative to multi-cast delegates that has separate equality keys.
///
/// The type of delegate to store.
///
/// While this type is immutable (via copies), creating a copy (via add or remove) is not currently thread safe.
/// This is in contrast to multi-cast delegate.
///
internal struct InvokeList
{
private Entry[]? _entries;
public int Count => _entries?.Length ?? 0;
///
/// Add an entry to the current invoke list, mutating it.
///
/// Actual value to store.
/// Equality comparison key.
public void AddInPlace(T value, object equality)
{
this = Add(value, equality);
}
///
/// Add an entry to the invoke list, returning a new instance. The original list is not modified.
///
/// Actual value to store.
/// Equality comparison key.
public readonly InvokeList Add(T value, object equality)
{
if (_entries == null)
{
return new InvokeList
{
_entries = new[]
{
new Entry { Value = value, Equality = equality }
}
};
}
var arr = _entries;
Array.Resize(ref arr, arr.Length + 1);
arr[^1] = new Entry { Value = value, Equality = equality };
return new InvokeList
{
_entries = arr
};
}
///
/// Remove an entry from the current invoke list, mutating it.
///
/// Equality comparison key.
public void RemoveInPlace(object equality)
{
this = Remove(equality);
}
///
/// Remove an entry from the invoke list, returning a new instance. The original list is not modified.
///
/// Equality comparison key.
public readonly InvokeList Remove(object equality)
{
if (_entries == null)
return this;
// Find if we even have this key in the list.
var entryIdx = -1;
for (var i = 0; i < _entries.Length; i++)
{
var entry = _entries[i];
if (equality.Equals(entry.Equality))
{
entryIdx = i;
break;
}
}
// Entry not in the list, copy is identical.
if (entryIdx < 0)
return this;
// Would remove the last element from the array, new instance is empty.
if (_entries.Length == 1)
return default;
// Create new backing array and copy stuff into it.
var newEntries = new Entry[_entries.Length - 1];
for (int srcIdx = 0, dstIdx = 0; dstIdx < newEntries.Length; srcIdx++, dstIdx++)
{
if (srcIdx == entryIdx)
srcIdx++;
newEntries[dstIdx] = _entries[srcIdx];
}
return new InvokeList
{
_entries = newEntries
};
}
public ReadOnlySpan Entries => _entries;
public struct Entry
{
public T? Value;
public object? Equality;
}
}