Last active
September 14, 2025 14:44
-
-
Save okankayhan/2582516e5b7541b152194698daa5e262 to your computer and use it in GitHub Desktop.
A fixed size list for deterministic games that uses MemoryPack. Can be modified for any other serializer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [MemoryPackable(GenerateType.CircularReference, SerializeLayout.Explicit)] | |
| public partial class DList<T> : IList<T> | |
| { | |
| [MemoryPackInclude][MemoryPackOrder(0)] private int _count; | |
| [MemoryPackInclude][MemoryPackOrder(1)] private T[] _buffer; | |
| /// <summary> | |
| /// Current number of elements in the list | |
| /// </summary> | |
| [MemoryPackIgnore] public int Count => _count; | |
| [MemoryPackIgnore] public bool IsReadOnly => false; | |
| /// <summary> | |
| /// Current capacity of the internal buffer | |
| /// </summary> | |
| [MemoryPackIgnore] public int Capacity => _buffer.Length; | |
| /// <summary> | |
| /// Whether the list is empty (no elements) | |
| /// </summary> | |
| [MemoryPackIgnore] public bool IsEmpty => _count == 0; | |
| /// <summary> | |
| /// Whether the list is full (no more space for new elements) | |
| /// </summary> | |
| [MemoryPackIgnore] public bool IsFull => _count >= _buffer.Length; | |
| [MemoryPackConstructor] | |
| private DList() | |
| { | |
| throw new NotImplementedException(); | |
| } | |
| /// <summary> | |
| /// Initializes a new instance with specified capacity | |
| /// </summary> | |
| /// <param name="capacity">Fixed capacity</param> | |
| public DList(int capacity) | |
| { | |
| if (capacity < 0) | |
| throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity cannot be negative"); | |
| _buffer = new T[capacity]; | |
| _count = 0; | |
| } | |
| /// <summary> | |
| /// Initializes a new instance with elements from a collection | |
| /// </summary> | |
| /// <param name="collection">Collection to copy elements from</param> | |
| /// <param name="capacity">Fixed capacity</param> | |
| public DList(IEnumerable<T> collection, int capacity) | |
| { | |
| if (collection == null) | |
| throw new ArgumentNullException(nameof(collection)); | |
| if (capacity < 0) | |
| throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity cannot be negative"); | |
| _buffer = new T[capacity]; | |
| _count = 0; | |
| foreach (var item in collection) | |
| { | |
| if (item != null && _count < capacity) | |
| { | |
| _buffer[_count] = item; | |
| _count++; | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Gets or sets the element at the specified index | |
| /// </summary> | |
| /// <param name="index">Zero-based index</param> | |
| /// <returns>Element at the specified index</returns> | |
| public T this[int index] | |
| { | |
| get | |
| { | |
| if (index < 0 || index >= _count) | |
| throw new ArgumentOutOfRangeException(nameof(index)); | |
| return _buffer[index]; | |
| } | |
| set | |
| { | |
| if (index < 0 || index >= _count) | |
| throw new ArgumentOutOfRangeException(nameof(index)); | |
| if (value == null) | |
| throw new ArgumentNullException(nameof(value), "Cannot set null value directly. Use Remove."); | |
| _buffer[index] = value; | |
| } | |
| } | |
| /// <summary> | |
| /// Adds an element to the end of the list if there's space | |
| /// </summary> | |
| /// <param name="item">Item to add</param> | |
| public void Add(T item) | |
| { | |
| if (item == null || _count >= _buffer.Length) | |
| { | |
| throw new InvalidOperationException("Cannot add item: either item is null or list is full."); | |
| } | |
| _buffer[_count] = item; | |
| _count++; | |
| } | |
| /// <summary> | |
| /// Inserts an element at the specified index if there's space | |
| /// </summary> | |
| /// <param name="index">Zero-based index</param> | |
| /// <param name="item">Item to insert</param> | |
| public void Insert(int index, T item) | |
| { | |
| if (item == null || index < 0 || index > _count || _count >= _buffer.Length) | |
| { | |
| throw new InvalidOperationException("Cannot insert item: either item is null, index is out of range, or list is full."); | |
| } | |
| ; | |
| // Shift elements to the right | |
| for (int i = _count; i > index; i--) | |
| { | |
| _buffer[i] = _buffer[i - 1]; | |
| } | |
| _buffer[index] = item; | |
| _count++; | |
| } | |
| /// <summary> | |
| /// Removes the first occurrence of the specified item | |
| /// </summary> | |
| /// <param name="item">Item to remove</param> | |
| /// <returns>True if item was found and removed</returns> | |
| public bool Remove(T item) | |
| { | |
| int index = IndexOf(item); | |
| if (index >= 0) | |
| { | |
| RemoveAt(index); | |
| return true; | |
| } | |
| return false; | |
| } | |
| /// <summary> | |
| /// Removes the element at the specified index | |
| /// </summary> | |
| /// <param name="index">Zero-based index</param> | |
| public void RemoveAt(int index) | |
| { | |
| if (index < 0 || index >= _count) | |
| throw new ArgumentOutOfRangeException(nameof(index)); | |
| // Shift elements to the left | |
| for (int i = index; i < _count - 1; i++) | |
| { | |
| _buffer[i] = _buffer[i + 1]; | |
| } | |
| _count--; | |
| } | |
| /// <summary> | |
| /// Removes all elements that match the predicate | |
| /// </summary> | |
| /// <param name="predicate">Predicate to test elements</param> | |
| /// <returns>Number of elements removed</returns> | |
| public int RemoveAll(Predicate<T> predicate) | |
| { | |
| if (predicate == null) | |
| throw new ArgumentNullException(nameof(predicate)); | |
| int removedCount = 0; | |
| int writeIndex = 0; | |
| for (int readIndex = 0; readIndex < _count; readIndex++) | |
| { | |
| if (!predicate(_buffer[readIndex])) | |
| { | |
| if (writeIndex != readIndex) | |
| { | |
| _buffer[writeIndex] = _buffer[readIndex]; | |
| } | |
| writeIndex++; | |
| } | |
| else | |
| { | |
| removedCount++; | |
| } | |
| } | |
| _count = writeIndex; | |
| return removedCount; | |
| } | |
| /// <summary> | |
| /// Removes all elements from the list | |
| /// </summary> | |
| public void Clear() | |
| { | |
| _count = 0; | |
| } | |
| /// <summary> | |
| /// Determines whether the list contains the specified item | |
| /// </summary> | |
| /// <param name="item">Item to locate</param> | |
| /// <returns>True if item is found</returns> | |
| public bool Contains(T item) | |
| { | |
| return IndexOf(item) >= 0; | |
| } | |
| /// <summary> | |
| /// Returns the zero-based index of the first occurrence of the item | |
| /// </summary> | |
| /// <param name="item">Item to locate</param> | |
| /// <returns>Zero-based index, or -1 if not found</returns> | |
| public int IndexOf(T item) | |
| { | |
| for (int i = 0; i < _count; i++) | |
| { | |
| if (EqualityComparer<T>.Default.Equals(_buffer[i], item)) | |
| return i; | |
| } | |
| return -1; | |
| } | |
| /// <summary> | |
| /// Returns the zero-based index of the last occurrence of the item | |
| /// </summary> | |
| /// <param name="item">Item to locate</param> | |
| /// <returns>Zero-based index, or -1 if not found</returns> | |
| public int LastIndexOf(T item) | |
| { | |
| for (int i = _count - 1; i >= 0; i--) | |
| { | |
| if (EqualityComparer<T>.Default.Equals(_buffer[i], item)) | |
| return i; | |
| } | |
| return -1; | |
| } | |
| public ReadOnlySpan<T> AsReadOnlySpan() | |
| { | |
| return _buffer.AsSpan(0, _count); | |
| } | |
| /// <summary> | |
| /// Copies the elements to a new array (allocates new array) | |
| /// </summary> | |
| /// <returns>New array containing all elements</returns> | |
| public T[] ToArray() | |
| { | |
| var result = new T[_count]; | |
| Array.Copy(_buffer, 0, result, 0, _count); | |
| return result; | |
| } | |
| /// <summary> | |
| /// Copies elements to an existing array (no allocation) | |
| /// </summary> | |
| /// <param name="array">Destination array</param> | |
| /// <param name="arrayIndex">Starting index in destination array</param> | |
| public void CopyTo(T[] array, int arrayIndex = 0) | |
| { | |
| if (array == null) | |
| throw new ArgumentNullException(nameof(array)); | |
| if (arrayIndex < 0 || arrayIndex > array.Length) | |
| throw new ArgumentOutOfRangeException(nameof(arrayIndex)); | |
| if (array.Length - arrayIndex < _count) | |
| throw new ArgumentException("Destination array is too small"); | |
| Array.Copy(_buffer, 0, array, arrayIndex, _count); | |
| } | |
| /// <summary> | |
| /// Finds the first element that matches the predicate | |
| /// </summary> | |
| /// <param name="predicate">Predicate to test elements</param> | |
| /// <returns>First matching element, or default(T) if not found</returns> | |
| public T Find(Predicate<T> predicate) | |
| { | |
| if (predicate == null) | |
| throw new ArgumentNullException(nameof(predicate)); | |
| for (int i = 0; i < _count; i++) | |
| { | |
| if (predicate(_buffer[i])) | |
| return _buffer[i]; | |
| } | |
| return default(T); | |
| } | |
| /// <summary> | |
| /// Sorts the elements using the default comparer | |
| /// </summary> | |
| public void Sort() | |
| { | |
| Sort(0, _count, Comparer<T>.Default); | |
| } | |
| /// <summary> | |
| /// Sorts the elements using a custom comparer | |
| /// </summary> | |
| /// <param name="comparer">Comparer to use for sorting</param> | |
| public void Sort(IComparer<T> comparer) | |
| { | |
| Sort(0, _count, comparer); | |
| } | |
| /// <summary> | |
| /// Sorts a range of elements | |
| /// </summary> | |
| /// <param name="index">Starting index</param> | |
| /// <param name="count">Number of elements to sort</param> | |
| /// <param name="comparer">Comparer to use for sorting</param> | |
| public void Sort(int index, int count, IComparer<T> comparer) | |
| { | |
| if (index < 0 || count < 0 || index + count > _count) | |
| throw new ArgumentOutOfRangeException(); | |
| if (comparer == null) | |
| comparer = Comparer<T>.Default; | |
| InsertionSort(index, count, comparer); | |
| } | |
| private void InsertionSort(int index, int count, IComparer<T> comparer) | |
| { | |
| for (int i = index + 1; i < index + count; i++) | |
| { | |
| T key = _buffer[i]; | |
| int j = i - 1; | |
| // Move elements greater than key one position ahead | |
| while (j >= index && comparer.Compare(_buffer[j], key) > 0) | |
| { | |
| _buffer[j + 1] = _buffer[j]; | |
| j--; | |
| } | |
| _buffer[j + 1] = key; | |
| } | |
| } | |
| #region IEnumerable<T> Implementation | |
| /// <summary> | |
| /// Returns an enumerator that iterates through the collection | |
| /// </summary> | |
| /// <returns>Enumerator for the collection</returns> | |
| public IEnumerator<T> GetEnumerator() | |
| { | |
| for (int i = 0; i < _count; i++) | |
| { | |
| yield return _buffer[i]; | |
| } | |
| } | |
| IEnumerator IEnumerable.GetEnumerator() | |
| { | |
| return GetEnumerator(); | |
| } | |
| #endregion | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment