< Summary

Line coverage
98%
Covered lines: 236
Uncovered lines: 3
Coverable lines: 239
Total lines: 574
Line coverage: 98.7%
Branch coverage
93%
Covered branches: 82
Total branches: 88
Branch coverage: 93.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .cctor()100%11100%
File 2: .cctor()100%11100%
File 2: .ctor()100%11100%
File 2: .ctor(...)100%44100%
File 2: .ctor(...)87.5%88100%
File 2: .ctor(...)100%11100%
File 2: get_InnerArray()100%11100%
File 2: get_Count()100%11100%
File 2: get_Capacity()100%11100%
File 2: System.Collections.Generic.ICollection<T>.get_IsReadOnly()100%11100%
File 2: get_IsSynchronized()100%11100%
File 2: System.Collections.ICollection.get_SyncRoot()100%22100%
File 2: get_Item(...)100%11100%
File 2: set_Item(...)100%11100%
File 2: get_State()100%11100%
File 2: set_State(...)66.66%66100%
File 2: System.Collections.Generic.ICollection<T>.Add(...)100%11100%
File 2: Push(...)100%22100%
File 2: PushRange(...)100%44100%
File 2: System.Collections.Generic.ICollection<T>.Remove(...)100%11100%
File 2: Pop()100%44100%
File 2: Clear()100%44100%
File 2: FastClear()100%11100%
File 2: EnsureCapacity(...)100%22100%
File 2: Resize(...)100%44100%
File 2: TrimCapacity()75%44100%
File 2: Peek()100%22100%
File 2: ToString()100%22100%
File 2: AsSpan()100%11100%
File 2: AsReadOnlySpan()100%11100%
File 2: CopyTo(...)100%44100%
File 2: CopyTo(...)100%22100%
File 2: System.Collections.ICollection.CopyTo(...)80%111078.57%
File 2: CloneTo(...)100%22100%
File 2: Contains(...)100%44100%
File 2: Exists(...)100%44100%
File 2: Find(...)100%44100%
File 2: GetEnumerator()100%11100%
File 2: System.Collections.Generic.IEnumerable<T>.GetEnumerator()100%11100%
File 2: System.Collections.IEnumerable.GetEnumerator()100%11100%
File 2: .ctor(...)100%11100%
File 2: get_Current()100%11100%
File 2: System.Collections.IEnumerator.get_Current()100%22100%
File 2: MoveNext()100%66100%
File 2: Reset()100%22100%
File 2: Dispose()100%11100%

File(s)

/_/src/SwiftCollections/obj/Debug/net8.0/MemoryPack.Generator/MemoryPack.Generator.MemoryPackGenerator/SwiftCollections.SwiftStack_T_.MemoryPackFormatter.g.cs

File '/_/src/SwiftCollections/obj/Debug/net8.0/MemoryPack.Generator/MemoryPack.Generator.MemoryPackGenerator/SwiftCollections.SwiftStack_T_.MemoryPackFormatter.g.cs' does not exist (any more).

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftStack.cs

#LineLine coverage
 1using MemoryPack;
 2using System;
 3using System.Collections;
 4using System.Collections.Generic;
 5using System.Runtime.CompilerServices;
 6using System.Text.Json.Serialization;
 7
 8namespace SwiftCollections;
 9
 10/// <summary>
 11/// Represents a fast, array-based stack (LIFO - Last-In-First-Out) collection of objects.
 12/// <para>
 13/// The <c>SwiftStack&lt;T&gt;</c> class provides O(1) time complexity for <c>Push</c> and <c>Pop</c> operations,
 14/// making it highly efficient for scenarios where performance is critical.
 15/// It minimizes memory allocations by reusing internal arrays and offers methods
 16/// like <c>FastClear</c> to quickly reset the stack without deallocating memory.
 17/// </para>
 18/// <para>
 19/// This implementation is optimized for performance and does not perform versioning checks.
 20/// Modifying the stack during enumeration may result in undefined behavior.
 21/// </para>
 22/// </summary>
 23/// <typeparam name="T">Specifies the type of elements in the stack.</typeparam>
 24[Serializable]
 25[JsonConverter(typeof(SwiftStateJsonConverterFactory))]
 26[MemoryPackable]
 27public sealed partial class SwiftStack<T> : ISwiftCloneable<T>, IEnumerable<T>, IEnumerable, ICollection<T>, ICollection
 28{
 29    #region Constants
 30
 31    /// <summary>
 32    /// The default initial capacity of the SwiftStack if none is specified.
 33    /// Used to allocate a reasonable starting size to minimize resizing operations.
 34    /// </summary>
 35    public const int DefaultCapacity = 8;
 36
 337    private static readonly T[] _emptyArray = Array.Empty<T>();
 338    private static readonly bool _clearReleasedSlots = RuntimeHelpers.IsReferenceOrContainsReferences<T>();
 39
 40    #endregion
 41
 42    #region Fields
 43
 44    /// <summary>
 45    /// The internal array that stores elements of the SwiftStack. Resized as needed to
 46    /// accommodate additional elements. Not directly exposed outside the stack.
 47    /// </summary>
 48    private T[] _innerArray;
 49
 50    /// <summary>
 51    /// The current number of elements in the SwiftStack. Represents the total count of
 52    /// valid elements stored in the stack, also indicating the arrayIndex of the next insertion point.
 53    /// </summary>
 54    private int _count;
 55
 56    [NonSerialized]
 57    private uint _version;
 58
 59    [NonSerialized]
 60    private object _syncRoot;
 61
 62    #endregion
 63
 64    #region Constructors
 65
 66    /// <summary>
 67    /// Initializes a new, empty instance of SwiftStack.
 68    /// </summary>
 14169    public SwiftStack() : this(0) { }
 70
 71    /// <summary>
 72    /// Initializes a new, empty instance of SwiftStack with the specified initial capacity.
 73    /// </summary>
 5174    public SwiftStack(int capacity)
 5175    {
 5176        if (capacity == 0)
 4777            _innerArray = _emptyArray;
 78        else
 479        {
 480            capacity = capacity <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(capacity);
 481            _innerArray = new T[capacity];
 482        }
 5183    }
 84
 385    public SwiftStack(IEnumerable<T> items)
 386    {
 387        SwiftThrowHelper.ThrowIfNull(items, nameof(items));
 88
 389        if (items is ICollection<T> collection)
 290        {
 291            int count = collection.Count;
 292            if (count == 0)
 193                _innerArray = _emptyArray;
 94            else
 195            {
 196                int capacity = SwiftHashTools.NextPowerOfTwo(count <= DefaultCapacity ? DefaultCapacity : count);
 197                _innerArray = new T[capacity];
 198                collection.CopyTo(_innerArray, 0);
 199                _count = count;
 1100            }
 2101        }
 102        else
 1103        {
 1104            _innerArray = new T[DefaultCapacity];
 9105            foreach (T item in items)
 3106                Push(item);
 1107        }
 3108    }
 109
 110    ///  <summary>
 111    ///  Initializes a new instance of the <see cref="SwiftStack{T}"/> class with the specified <see cref="SwiftArraySta
 112    ///  </summary>
 113    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 114    [MemoryPackConstructor]
 3115    public SwiftStack(SwiftArrayState<T> state)
 3116    {
 3117        State = state;
 3118    }
 119
 120    #endregion
 121
 122    #region Properties
 123
 124    /// <inheritdoc cref="_innerArray"/>
 125    [JsonIgnore]
 126    [MemoryPackIgnore]
 3127    public T[] InnerArray => _innerArray;
 128
 129    /// <inheritdoc cref="_count"/>
 130    [JsonIgnore]
 131    [MemoryPackIgnore]
 118132    public int Count => _count;
 133
 134    /// <summary>
 135    /// Gets the total number of elements the SwiftQueue can hold without resizing.
 136    /// Reflects the current allocated size of the internal array.
 137    /// </summary>
 138    [JsonIgnore]
 139    [MemoryPackIgnore]
 12140    public int Capacity => _innerArray.Length;
 141
 142    [JsonIgnore]
 143    [MemoryPackIgnore]
 1144    bool ICollection<T>.IsReadOnly => false;
 145
 146    [JsonIgnore]
 147    [MemoryPackIgnore]
 1148    public bool IsSynchronized => false;
 149
 150    [JsonIgnore]
 151    [MemoryPackIgnore]
 1152    object ICollection.SyncRoot => _syncRoot ??= new object();
 153
 154    /// <summary>
 155    /// Gets the element at the specified arrayIndex.
 156    /// </summary>
 157    [JsonIgnore]
 158    [MemoryPackIgnore]
 159    public T this[int index]
 160    {
 161        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 162        get
 204163        {
 204164            SwiftThrowHelper.ThrowIfIndexInvalid(index, _count);
 203165            return _innerArray[index];
 203166        }
 167        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 168        set
 1169        {
 1170            SwiftThrowHelper.ThrowIfIndexInvalid(index, _count);
 1171            _innerArray[index] = value;
 1172        }
 173    }
 174
 175    [JsonInclude]
 176    [MemoryPackInclude]
 177    public SwiftArrayState<T> State
 178    {
 179        get
 2180        {
 2181            var items = new T[_count];
 2182            Array.Copy(_innerArray, 0, items, 0, _count);
 2183            return new SwiftArrayState<T>(items);
 2184        }
 185        internal set
 3186        {
 3187            int count = value.Items?.Length ?? 0;
 188
 3189            if (count == 0)
 1190            {
 1191                _innerArray = _emptyArray;
 1192                _count = 0;
 1193                _version = 0;
 1194                return;
 195            }
 196
 2197            int capacity = SwiftHashTools.NextPowerOfTwo(count <= DefaultCapacity ? DefaultCapacity : count);
 198
 2199            _innerArray = new T[capacity];
 2200            Array.Copy(value.Items, 0, _innerArray, 0, count);
 201
 2202            _count = count;
 2203            _version = 0;
 3204        }
 205    }
 206
 207    #endregion
 208
 209    #region Collection Manipulation
 210
 211    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2212    void ICollection<T>.Add(T item) => Push(item);
 213
 214    /// <summary>
 215    /// Inserts an object at the top of the SwiftStack.
 216    /// </summary>
 217    /// <param name="item"></param>
 218    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 219    public void Push(T item)
 278220    {
 278221        if ((uint)_count == (uint)_innerArray.Length)
 41222            Resize(_innerArray.Length * 2);
 278223        _innerArray[_count++] = item;
 278224        _version++;
 278225    }
 226
 227    /// <summary>
 228    /// Pushes the elements of the specified span onto the stack in order.
 229    /// </summary>
 230    /// <param name="items">The span whose elements should be pushed.</param>
 231    public void PushRange(ReadOnlySpan<T> items)
 6232    {
 6233        if (items.Length == 0)
 1234            return;
 235
 5236        if (_count + items.Length > _innerArray.Length)
 4237        {
 4238            int newCapacity = SwiftHashTools.NextPowerOfTwo(_count + items.Length);
 4239            Resize(newCapacity);
 4240        }
 241
 5242        items.CopyTo(_innerArray.AsSpan(_count, items.Length));
 5243        _count += items.Length;
 5244        _version++;
 6245    }
 246
 247    bool ICollection<T>.Remove(T item)
 1248    {
 1249        throw new NotSupportedException("Remove is not supported on Stack.");
 250    }
 251
 252    /// <summary>
 253    /// Removes and returns the object at the top of the SwiftStack.
 254    /// </summary>
 255    /// <returns></returns>
 256    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 257    public T Pop()
 3258    {
 4259        if ((uint)_count == 0) throw new InvalidOperationException("Stack is empty.");
 2260        T item = _innerArray[--_count];
 2261        if (_clearReleasedSlots)
 1262            _innerArray[_count] = default;
 2263        _version++;
 2264        return item;
 2265    }
 266
 267    /// <summary>
 268    /// Removes all elements from the SwiftStack, resetting its count to zero.
 269    /// </summary>
 270    public void Clear()
 5271    {
 6272        if (_count == 0) return;
 4273        if (_clearReleasedSlots)
 1274            Array.Clear(_innerArray, 0, _count);
 4275        _count = 0;
 4276        _version++;
 5277    }
 278
 279    /// <summary>
 280    /// Clears the SwiftStack without releasing the reference to the stored elements.
 281    /// Use FastClear() when you want to quickly reset the list without reallocating memory.
 282    /// </summary>
 283    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 284    public void FastClear()
 1285    {
 1286        _count = 0;
 1287        _version++;
 1288    }
 289
 290    #endregion
 291
 292    #region Capacity Management
 293
 294    public void EnsureCapacity(int capacity)
 1295    {
 1296        capacity = SwiftHashTools.NextPowerOfTwo(capacity);
 1297        if (capacity > _innerArray.Length)
 1298            Resize(capacity);
 1299    }
 300
 301    /// <summary>
 302    /// Ensures that the capacity of the stack is sufficient to accommodate the specified number of elements.
 303    /// The stack capacity can increase by double to balance memory allocation efficiency and space.
 304    /// </summary>
 305    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 306    private void Resize(int newSize)
 46307    {
 46308        int newCapacity = newSize <= DefaultCapacity ? DefaultCapacity : newSize;
 46309        T[] newArray = new T[newCapacity];
 46310        if ((uint)_count > 0)
 8311            Array.Copy(_innerArray, 0, newArray, 0, _count);
 46312        _innerArray = newArray;
 46313        _version++;
 46314    }
 315
 316
 317    /// <summary>
 318    /// Sets the capacity of a <see cref="SwiftStack{T}"/> to the actual
 319    /// number of elements it contains, rounded up to a nearby next power of 2 value.
 320    /// </summary>
 321    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 322    public void TrimCapacity()
 2323    {
 2324        int newCapacity = _count <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(_count);
 2325        T[] newArray = new T[newCapacity];
 2326        if ((uint)_count > 0)
 1327            Array.Copy(_innerArray, 0, newArray, 0, _count);
 2328        _innerArray = newArray;
 2329        _version++;
 2330    }
 331
 332    #endregion
 333
 334    #region Utility Methods
 335
 336    /// <summary>
 337    /// Returns the object at the top of the SwiftStack without removing it.
 338    /// </summary>
 339    /// <returns></returns>
 340    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 341    public T Peek()
 7342    {
 8343        if ((uint)_count == 0) throw new InvalidOperationException("Stack is empty.");
 6344        return _innerArray[_count - 1];
 6345    }
 346
 347    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2348    public override string ToString() => (uint)_count == 0 ? $"{typeof(SwiftStack<T>)}: Empty" : $"{typeof(SwiftStack<T>
 349
 350    /// <summary>
 351    /// Returns a mutable span over the populated portion of the stack.
 352    /// </summary>
 353    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2354    public Span<T> AsSpan() => _innerArray.AsSpan(0, _count);
 355
 356    /// <summary>
 357    /// Returns a read-only span over the populated portion of the stack.
 358    /// </summary>
 359    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 4360    public ReadOnlySpan<T> AsReadOnlySpan() => _innerArray.AsSpan(0, _count);
 361
 362    public void CopyTo(T[] array, int arrayIndex)
 4363    {
 4364        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 4365        if ((uint)arrayIndex > array.Length) throw new ArgumentOutOfRangeException();
 3366        if ((uint)(array.Length - arrayIndex) < (uint)_count) throw new ArgumentException("Destination array is not long
 367
 1368        Array.Copy(_innerArray, 0, array, arrayIndex, _count);
 1369    }
 370
 371    /// <summary>
 372    /// Copies the populated elements of the SwiftStack into the specified destination span.
 373    /// </summary>
 374    /// <param name="destination">The destination span.</param>
 375    public void CopyTo(Span<T> destination)
 2376    {
 2377        if (destination.Length < _count)
 1378            throw new ArgumentException("Destination span is not long enough.", nameof(destination));
 379
 1380        AsSpan().CopyTo(destination);
 1381    }
 382
 383    void ICollection.CopyTo(Array array, int arrayIndex)
 3384    {
 3385        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 4386        if ((uint)array.Rank != 1) throw new ArgumentException("Array must be single dimensional.");
 3387        if ((uint)array.GetLowerBound(0) != 0) throw new ArgumentException("Array must have zero-based indexing.");
 1388        if ((uint)arrayIndex > array.Length) throw new ArgumentOutOfRangeException();
 1389        if ((uint)(array.Length - arrayIndex) < _count) throw new ArgumentException("Destination array is not long enoug
 390
 391        try
 1392        {
 8393            for (int i = 0; (uint)i < (uint)_count; i++)
 3394                array.SetValue(_innerArray[i], arrayIndex++);
 1395        }
 0396        catch (ArrayTypeMismatchException)
 0397        {
 0398            throw new ArgumentException("Invalid array type.");
 399        }
 1400    }
 401
 402    public void CloneTo(ICollection<T> output)
 1403    {
 1404        output.Clear();
 6405        for (int i = 0; (uint)i < (uint)_count; i++)
 2406            output.Add(_innerArray[i]);
 1407    }
 408
 409    public bool Contains(T item)
 2410    {
 2411        EqualityComparer<T> comparer = EqualityComparer<T>.Default;
 10412        for (int i = 0; i < _count; i++)
 4413        {
 4414            if (comparer.Equals(_innerArray[i], item))
 1415                return true;
 3416        }
 1417        return false;
 2418    }
 419
 420    /// <summary>
 421    /// Determines whether the <see cref="SwiftStack{T}"/> contains an element that matches the conditions defined by th
 422    /// </summary>
 423    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 424    /// <returns><c>true</c> if the <see cref="SwiftStack{T}"/> contains one or more elements that match the specified p
 425    public bool Exists(Predicate<T> match)
 3426    {
 3427        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 428
 12429        for (int i = _count - 1; i >= 0; i--)
 5430        {
 5431            if (match(_innerArray[i]))
 1432                return true;
 4433        }
 434
 1435        return false;
 2436    }
 437
 438    /// <summary>
 439    /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first ma
 440    /// </summary>
 441    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 442    /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, 
 443    public T Find(Predicate<T> match)
 2444    {
 2445        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 446
 8447        for (int i = _count - 1; i >= 0; i--)
 3448        {
 3449            if (match(_innerArray[i]))
 1450                return _innerArray[i];
 2451        }
 452
 1453        return default;
 2454    }
 455
 456    #endregion
 457
 458    #region Enumerators
 20459    public SwiftStackEnumerator GetEnumerator() => new SwiftStackEnumerator(this);
 9460    IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
 3461    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 462
 463    public struct SwiftStackEnumerator : IEnumerator<T>, IEnumerator, IDisposable
 464    {
 465        private readonly SwiftStack<T> _stack;
 466        private readonly T[] _array;
 467        private readonly uint _version;
 468        private readonly int _count;
 469        private int _index;
 470
 471        private T _current;
 472
 473        internal SwiftStackEnumerator(SwiftStack<T> stack)
 20474        {
 20475            _stack = stack;
 20476            _array = stack._innerArray;
 20477            _count = stack._count;
 20478            _version = stack._version;
 20479            _index = -2; // Enumerator not started
 20480            _current = default;
 20481        }
 482
 430483        public T Current => _current;
 484
 485        object IEnumerator.Current
 486        {
 487            get
 2488            {
 3489                if ((uint)_index > _count) throw new InvalidOperationException("Bad enumeration");
 1490                return _current;
 1491            }
 492        }
 493
 494        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 495        public bool MoveNext()
 251496        {
 251497            if (_version != _stack._version)
 1498                throw new InvalidOperationException("Enumerator modified outside of enumeration!");
 499
 250500            if (_index == -2)
 22501            {
 22502                _index = _count - 1;
 22503            }
 504            else
 228505            {
 228506                _index--;
 228507            }
 508
 250509            if (_index >= 0)
 232510            {
 232511                _current = _array[_index];
 232512                return true;
 513            }
 514
 18515            _index = -1;
 18516            _current = default;
 18517            return false;
 250518        }
 519
 520        public void Reset()
 4521        {
 4522            if (_version != _stack._version)
 1523                throw new InvalidOperationException("Enumerator modified outside of enumeration!");
 524
 3525            _index = -2;
 3526            _current = default;
 3527        }
 528
 11529        public void Dispose() => _index = -1;
 530    }
 531
 532    #endregion
 533}