// SPDX-FileCopyrightText: Copyright 2024 Fabio Iotti // SPDX-License-Identifier: AGPL-3.0-only using System.Buffers; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace CommentsEngine.Data; class ArrayPoolList : IList, IDisposable { readonly ArrayPool _pool; readonly bool _clearArray; int _count; T[]? _buffer; public ArrayPoolList() : this(16) { } public ArrayPoolList(int capacity) : this(capacity, ArrayPool.Shared) { } public ArrayPoolList(ArrayPool pool, bool clearArray = false) : this(16, pool, clearArray) { } public ArrayPoolList(int capacity, ArrayPool pool, bool clearArray = false) { _pool = pool; _clearArray = clearArray; _buffer = _pool.Rent(capacity); } /// public T this[int index] { get { CheckNotDisposed(); ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(index, _count); return _buffer[index]; } set { CheckNotDisposed(); ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(index, _count); _buffer[index] = value; } } /// public int Count => _count; bool ICollection.IsReadOnly => false; [MethodImpl(MethodImplOptions.AggressiveInlining)] [MemberNotNull(nameof(_buffer))] void CheckNotDisposed() => ObjectDisposedException.ThrowIf(_buffer is null, typeof(ArrayPoolList)); [MethodImpl(MethodImplOptions.AggressiveInlining)] void EnsureBufferAtLeast(ref T[] _buffer, int size) { if (_buffer.Length < size) { T[] newBuffer = _pool.Rent(1 + size * 2); _buffer.CopyTo(newBuffer, 0); _pool.Return(_buffer, _clearArray); _buffer = newBuffer; } } /// public void Add(T item) { CheckNotDisposed(); EnsureBufferAtLeast(ref _buffer, _count + 1); _buffer[_count++] = item; } /// public void Clear() { CheckNotDisposed(); _count = 0; } /// public bool Contains(T item) => IndexOf(item) is not -1; /// public void CopyTo(T[] array, int arrayIndex) { CheckNotDisposed(); _buffer.AsSpan(0, _count).CopyTo(array.AsSpan(arrayIndex)); } /// public int IndexOf(T item) { CheckNotDisposed(); for (int i = 0; i < _buffer.Length && i < _count; i++) if (Equals(item, _buffer[i])) return i; return -1; } /// public void Insert(int index, T item) { CheckNotDisposed(); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(index); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(_count, index); EnsureBufferAtLeast(ref _buffer, _count + 1); Array.Copy(_buffer, index, _buffer, index + 1, _count - index); _buffer[index] = item; _count++; } /// public bool Remove(T item) { CheckNotDisposed(); int index = IndexOf(item); if (index is -1) return false; Array.Copy(_buffer, index, _buffer, index - 1, _count - index); _count--; return true; } /// public void RemoveAt(int index) { CheckNotDisposed(); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(index); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(_count, index); Array.Copy(_buffer, index, _buffer, index - 1, _count - index); _count--; } /// public IEnumerator GetEnumerator() { CheckNotDisposed(); return _buffer.Take(_count).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); protected virtual void Dispose(bool disposing) { if (_buffer is not null) { if (disposing) _pool.Return(_buffer, _clearArray); _buffer = null; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } }