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; } }