mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-01 17:47:24 +02:00
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
233 lines
6.0 KiB
C#
233 lines
6.0 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace Robust.Shared.Utility
|
|
{
|
|
/// <summary>
|
|
/// Supposedly a high-performance version of <see cref="List{T}"/>,
|
|
/// that allows fetching direct references to the underlying contents.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Due to this type's nature,
|
|
/// keeping references to the contents of this list while mutating it is undefined behavior.
|
|
/// Don't do it.
|
|
/// </remarks>
|
|
/// <typeparam name="T">The type of the contents of the list. This must be an unmanaged type.</typeparam>
|
|
public sealed class RefList<T> : IList<T>
|
|
{
|
|
private T[] _array;
|
|
private int _size;
|
|
|
|
public RefList() : this(1)
|
|
{
|
|
}
|
|
|
|
public RefList(int initialCapacity)
|
|
{
|
|
_array = new T[initialCapacity];
|
|
_size = 0;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Enumerator GetEnumerator()
|
|
{
|
|
return new(this);
|
|
}
|
|
|
|
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allocate a new member in the list and return the reference to it for initialization.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public ref T AllocAdd()
|
|
{
|
|
_ensureCapacity(_size+1);
|
|
|
|
return ref _array[_size++];
|
|
}
|
|
|
|
/// <summary>
|
|
/// It is probably advisable to use <see cref="AllocAdd"/> instead for better performance.
|
|
/// </summary>
|
|
public void Add(T item)
|
|
{
|
|
_ensureCapacity(_size+1);
|
|
|
|
_array[_size++] = item;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void Clear()
|
|
{
|
|
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
|
{
|
|
Array.Clear(_array, 0, _size);
|
|
}
|
|
_size = 0;
|
|
}
|
|
|
|
public bool Contains(T item)
|
|
{
|
|
return IndexOf(item) != -1;
|
|
}
|
|
|
|
public void CopyTo(T[] array, int arrayIndex)
|
|
{
|
|
Array.Copy(_array, 0, array, arrayIndex, _size);
|
|
}
|
|
|
|
public bool Remove(T item)
|
|
{
|
|
var index = IndexOf(item);
|
|
if (index == -1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RemoveAt(index);
|
|
return true;
|
|
}
|
|
|
|
public int Count => _size;
|
|
public bool IsReadOnly => false;
|
|
public int Capacity => _array.Length;
|
|
|
|
public int IndexOf(T item)
|
|
{
|
|
return Array.IndexOf(_array, item, 0, _size);
|
|
}
|
|
|
|
public void Insert(int index, T item)
|
|
{
|
|
_ensureCapacity(_size+1);
|
|
|
|
if (index < _size)
|
|
{
|
|
Array.Copy(_array, index, _array, index+1, _size - index);
|
|
}
|
|
|
|
_array[index] = item;
|
|
_size++;
|
|
}
|
|
|
|
public void TrimCapacity(int capacity)
|
|
{
|
|
if (Count > capacity)
|
|
throw new ArgumentException("Cannot trim past list contents");
|
|
|
|
var oldArr = _array;
|
|
_array = new T[capacity];
|
|
|
|
oldArr.AsSpan(0, _size).CopyTo(_array);
|
|
}
|
|
|
|
public void RemoveAt(int index)
|
|
{
|
|
if (index >= _size)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(index), index, "Index must fit into list.");
|
|
}
|
|
|
|
_size -= 1;
|
|
// No need to do a copy if the last element gets removed.
|
|
if (index < _size)
|
|
{
|
|
Array.Copy(_array, index + 1, _array, index, _size - index);
|
|
}
|
|
|
|
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
|
{
|
|
_array[_size] = default!;
|
|
}
|
|
}
|
|
|
|
public ref T this[int index]
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
get => ref _array[index];
|
|
}
|
|
|
|
T IList<T>.this[int index]
|
|
{
|
|
get => _array[index];
|
|
set => _array[index] = value;
|
|
}
|
|
|
|
public void Sort(IComparer<T> comparer)
|
|
{
|
|
Array.Sort(_array, 0, _size, comparer);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Span<T> GetSpan()
|
|
{
|
|
return new(_array, 0, _size);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void _ensureCapacity(int newCapacity)
|
|
{
|
|
if (newCapacity < _array.Length)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var old = _array;
|
|
_array = new T[old.Length * 2];
|
|
Array.Copy(old, 0, _array, 0, _size);
|
|
}
|
|
|
|
public struct Enumerator : IEnumerator<T>
|
|
{
|
|
private readonly RefList<T> _owner;
|
|
private int _position;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public Enumerator(RefList<T> owner)
|
|
{
|
|
_owner = owner;
|
|
_position = -1;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool MoveNext()
|
|
{
|
|
return ++_position < _owner._size;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void Reset()
|
|
{
|
|
_position = 0;
|
|
}
|
|
|
|
public ref T Current
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
get => ref _owner._array[_position];
|
|
}
|
|
|
|
T IEnumerator<T>.Current => Current;
|
|
|
|
object? IEnumerator.Current => Current;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void Dispose()
|
|
{
|
|
// Nada, at least nothing yet.
|
|
}
|
|
}
|
|
}
|
|
}
|