< Summary

Information
Class: SwiftCollections.SwiftBucket<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftBucket.cs
Line coverage
91%
Covered lines: 300
Uncovered lines: 27
Coverable lines: 327
Total lines: 684
Line coverage: 91.7%
Branch coverage
76%
Covered branches: 117
Total branches: 152
Branch coverage: 76.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
.ctor(...)100%22100%
.ctor(...)100%11100%
get_Count()100%11100%
get_PeakCount()100%11100%
get_Capacity()100%11100%
get_Item(...)100%22100%
set_Item(...)50%22100%
get_IsReadOnly()100%11100%
get_IsSynchronized()100%11100%
get_SyncRoot()100%22100%
get_State()100%44100%
set_State(...)57.69%362675.67%
Add(...)100%44100%
System.Collections.Generic.ICollection<T>.Add(...)100%11100%
InsertAt(...)83.33%6692.85%
TryRemove(...)100%22100%
System.Collections.Generic.ICollection<T>.Remove(...)100%11100%
TryRemoveAt(...)100%22100%
RemoveAt(...)100%11100%
Clear()100%44100%
EnsureCapacity(...)100%22100%
Resize(...)75%44100%
TrimExcessCapacity()100%44100%
GetLivePeakCount()50%4483.33%
RebuildFreeIndices()100%44100%
TryGetValue(...)100%22100%
Contains(...)100%11100%
Exists(...)75%8892.85%
Find(...)100%88100%
IsAllocated(...)100%22100%
IndexOf(...)50%461856%
CopyTo(...)80%1010100%
System.Collections.ICollection.CopyTo(...)71.42%151485.71%
CloneTo(...)100%66100%
GetEnumerator()100%11100%
System.Collections.Generic.IEnumerable<T>.GetEnumerator()100%11100%
System.Collections.IEnumerable.GetEnumerator()100%11100%
.ctor(...)100%11100%
get_Current()100%11100%
System.Collections.IEnumerator.get_Current()50%22100%
MoveNext()100%66100%
Reset()50%2283.33%
Dispose()100%11100%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftBucket.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 high-performance bucket collection that assigns and manages stable integer indices
 12/// for stored items. Provides O(1) insertion, removal, and lookup by internally generated index.
 13/// </summary>
 14/// <remarks>
 15/// Unlike <see cref="SwiftSparseMap{T}"/>, which requires callers to provide the key used to store values,
 16/// <see cref="SwiftBucket{T}"/> internally generates and manages indices for each inserted item.
 17///
 18/// These indices remain stable for the lifetime of the item unless it is removed.
 19///
 20/// The container is optimized for scenarios requiring:
 21/// <list type="bullet">
 22///     <item>
 23///         <description>Stable handles or identifiers.</description>
 24///     </item>
 25///     <item>
 26///         <description>Fast addition and removal.</description>
 27///     </item>
 28///     <item>
 29///         <description>Dense storage and iteration performance.</description>
 30///     </item>
 31/// </list>
 32///
 33/// **Efficient Lookups Using Indices**:
 34/// When you add items to the bucket using the <see cref="Add"/> method, it returns an arrayIndex that you can store ext
 35/// You can then use this arrayIndex to access the item directly via the indexer, and check if it's still present using 
 36/// This approach allows for O(1) time complexity for lookups and existence checks, avoiding the need for O(n) searches 
 37///
 38/// **Note**: iteration over the collection does not follow any guaranteed order and depends on internal allocation.
 39/// </remarks>
 40/// <typeparam name="T">Specifies the type of elements in the bucket.</typeparam>
 41[Serializable]
 42[JsonConverter(typeof(SwiftStateJsonConverterFactory))]
 43[MemoryPackable]
 44public sealed partial class SwiftBucket<T> : ISwiftCloneable<T>, IEnumerable<T>, ICollection<T>, ICollection
 45{
 46    #region Constants
 47
 48    public const int DefaultCapacity = 8;
 49
 50    #endregion
 51
 52    #region Fields
 53
 54    private Entry[] _innerArray;
 55
 56    private int _count;
 57
 58    private int _peakCount;
 59
 60    private SwiftIntStack _freeIndices;
 61
 62    [NonSerialized]
 63    private uint _version;
 64
 65    [NonSerialized]
 66    private object _syncRoot;
 67
 68    #endregion
 69
 70    #region Nested Types
 71
 72    [Serializable]
 73    private struct Entry
 74    {
 75        public T Value;
 76        public bool IsUsed;
 77    }
 78
 79    #endregion
 80
 81    #region Constructors
 82
 83    /// <summary>
 84    /// Initializes a new instance of the <see cref="SwiftBucket{T}"/> class.
 85    /// </summary>
 8486    public SwiftBucket() : this(DefaultCapacity) { }
 87
 88    /// <summary>
 89    /// Initializes a new instance of the <see cref="SwiftBucket{T}"/> class with the specified capacity.
 90    /// </summary>
 91    /// <param name="capacity">The initial capacity of the bucket.</param>
 3392    public SwiftBucket(int capacity)
 3393    {
 3394        capacity = capacity <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(capacity);
 3395        _innerArray = new Entry[capacity];
 3396        _freeIndices = new SwiftIntStack(SwiftIntStack.DefaultCapacity);
 3397    }
 98
 99    ///  <summary>
 100    ///  Initializes a new instance of the <see cref="SwiftBucket{T}"/> class with the specified <see cref="SwiftArraySt
 101    ///  </summary>
 102    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 103    [MemoryPackConstructor]
 3104    public SwiftBucket(SwiftBucketState<T> state)
 3105    {
 3106        State = state;
 3107    }
 108
 109    #endregion
 110
 111    #region Properties
 112
 113    /// <summary>
 114    /// Gets the number of elements contained in the <see cref="SwiftBucket{T}"/>.
 115    /// </summary>
 116    [JsonIgnore]
 117    [MemoryPackIgnore]
 117118    public int Count => _count;
 119
 120    [JsonIgnore]
 121    [MemoryPackIgnore]
 5122    public int PeakCount => _peakCount;
 123
 124    /// <summary>
 125    /// Gets the total capacity of the <see cref="SwiftBucket{T}"/>.
 126    /// </summary>
 127    [JsonIgnore]
 128    [MemoryPackIgnore]
 8129    public int Capacity => _innerArray.Length;
 130
 131    /// <summary>
 132    /// Gets or sets the element at the specified arrayIndex.
 133    /// Throws <see cref="ArgumentOutOfRangeException"/> if the arrayIndex is invalid or unallocated.
 134    /// </summary>
 135    /// <param name="index">The zero-based arrayIndex of the element to get or set.</param>
 136    [JsonIgnore]
 137    [MemoryPackIgnore]
 138    public T this[int index]
 139    {
 140        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 141        get
 223142        {
 226143            if (!IsAllocated(index)) throw new ArgumentOutOfRangeException(nameof(index));
 220144            return _innerArray[index].Value;
 220145        }
 146        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 147        set
 1148        {
 1149            if (!IsAllocated(index)) throw new ArgumentOutOfRangeException(nameof(index));
 1150            _innerArray[index].Value = value;
 1151            _version++;
 1152        }
 153    }
 154
 155    [JsonIgnore]
 156    [MemoryPackIgnore]
 1157    public bool IsReadOnly => false;
 158
 159    [JsonIgnore]
 160    [MemoryPackIgnore]
 1161    public bool IsSynchronized => false;
 162
 163    [JsonIgnore]
 164    [MemoryPackIgnore]
 1165    public object SyncRoot => _syncRoot ??= new object();
 166
 167    [JsonInclude]
 168    [MemoryPackInclude]
 169    public SwiftBucketState<T> State
 170    {
 171        get
 2172        {
 2173            int length = _innerArray.Length;
 174
 2175            var items = new T[length];
 2176            var allocated = new bool[length];
 177
 516178            for (int i = 0; i < length; i++)
 256179            {
 256180                if (_innerArray[i].IsUsed)
 200181                {
 200182                    items[i] = _innerArray[i].Value;
 200183                    allocated[i] = true;
 200184                }
 256185            }
 186
 2187            int[] free = new int[_freeIndices.Count];
 2188            Array.Copy(_freeIndices.Array, free, _freeIndices.Count);
 189
 2190            return new SwiftBucketState<T>(
 2191                items,
 2192                allocated,
 2193                free,
 2194                _peakCount
 2195            );
 2196        }
 197        internal set
 3198        {
 3199            var items = value.Items ?? Array.Empty<T>();
 3200            var allocated = value.Allocated ?? Array.Empty<bool>();
 3201            var freeIndices = value.FreeIndices ?? Array.Empty<int>();
 202
 3203            int sourceLength = Math.Max(items.Length, allocated.Length);
 3204            int capacity = sourceLength < DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(sourceLength
 205
 3206            _innerArray = new Entry[capacity];
 3207            _freeIndices = new SwiftIntStack(Math.Max(SwiftIntStack.DefaultCapacity, freeIndices.Length));
 208
 3209            _count = 0;
 3210            int maxReferencedIndex = -1;
 211
 524212            for (int i = 0; i < sourceLength; i++)
 259213            {
 259214                if (allocated.Length > i && allocated[i])
 202215                {
 202216                    if (items.Length > i)
 202217                        _innerArray[i].Value = items[i];
 218
 202219                    _innerArray[i].IsUsed = true;
 202220                    _count++;
 202221                    maxReferencedIndex = i;
 202222                }
 259223            }
 224
 9225            foreach (var index in freeIndices)
 0226            {
 0227                if ((uint)index >= (uint)capacity)
 0228                    throw new ArgumentOutOfRangeException(nameof(index), "Free index is out of range.");
 229
 0230                _freeIndices.Push(index);
 0231                if (index > maxReferencedIndex)
 0232                    maxReferencedIndex = index;
 0233            }
 234
 3235            int peakCount = value.PeakCount;
 3236            if (peakCount < 0)
 0237                peakCount = 0;
 238
 3239            _peakCount = Math.Max(peakCount, maxReferencedIndex + 1);
 3240            if (_peakCount > capacity)
 0241                _peakCount = capacity;
 242
 3243            _version = 0;
 3244        }
 245    }
 246
 247    #endregion
 248
 249    #region Collection Management
 250
 251    /// <summary>
 252    /// Adds an item to the bucket and returns its arrayIndex.
 253    /// </summary>
 254    /// <param name="item">The item to add.</param>
 255    /// <returns>The arrayIndex where the item was added.</returns>
 256    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 257    public int Add(T item)
 100274258    {
 259        int index;
 100274260        if ((uint)_freeIndices.Count == 0)
 100273261        {
 100273262            index = _peakCount++;
 100273263            if ((uint)index >= (uint)_innerArray.Length)
 24264                Resize(_innerArray.Length * 2);
 100273265        }
 1266        else index = _freeIndices.Pop();
 267
 100274268        _innerArray[index].Value = item;
 100274269        _innerArray[index].IsUsed = true;
 100274270        _count++;
 100274271        _version++;
 100274272        return index;
 100274273    }
 274
 2275    void ICollection<T>.Add(T item) => Add(item);
 276
 277    /// <summary>
 278    /// Inserts an item at the specified arrayIndex.
 279    /// If an item already exists at that arrayIndex, it will be replaced.
 280    /// </summary>
 281    /// <param name="index">The arrayIndex at which to insert the item.</param>
 282    /// <param name="item">The item to insert.</param>
 283    public void InsertAt(int index, T item)
 6284    {
 6285        SwiftThrowHelper.ThrowIfNegative(index, nameof(index));
 6286        if ((uint)index >= (uint)_innerArray.Length)
 0287            Resize(_innerArray.Length * 2);
 6288        if (!_innerArray[index].IsUsed)
 5289        {
 5290            _count++;
 5291            if ((uint)index >= (uint)_peakCount)
 5292                _peakCount = index + 1;
 5293        }
 6294        _innerArray[index].Value = item;
 6295        _innerArray[index].IsUsed = true;
 6296        _version++;
 6297    }
 298
 299    /// <summary>
 300    /// Removes the first occurrence of a specific object from the bucket.
 301    /// </summary>
 302    /// <param name="item">The object to remove.</param>
 303    /// <returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
 304    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 305    public bool TryRemove(T item)
 3306    {
 3307        int index = IndexOf(item);
 4308        if (index < 0) return false;
 2309        RemoveAt(index);
 2310        return true;
 3311    }
 312
 1313    bool ICollection<T>.Remove(T item) => TryRemove(item);
 314
 315    /// <summary>
 316    /// Removes the item at the specified arrayIndex if it has been allocated.
 317    /// </summary>
 318    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 319    public bool TryRemoveAt(int index)
 50004320    {
 50004321        if (IsAllocated(index))
 50003322        {
 50003323            RemoveAt(index);
 50003324            return true;
 325        }
 1326        return false;
 50004327    }
 328
 329    /// <summary>
 330    /// Removes the item at the specified arrayIndex.
 331    /// </summary>
 332    /// <param name="index">The arrayIndex of the item to remove.</param>
 333    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 334    public void RemoveAt(int index)
 50005335    {
 50005336        _innerArray[index] = default;
 50005337        _count--;
 50005338        _freeIndices.Push(index);
 50005339        _version++;
 50005340    }
 341
 342    /// <summary>
 343    /// Removes all items from the bucket.
 344    /// </summary>
 345    public void Clear()
 1346    {
 1347        if ((uint)_count == 0) return;
 8348        for (int i = 0; i < _peakCount; i++)
 3349            _innerArray[i] = default;
 1350        _freeIndices.Reset();
 1351        _count = 0;
 1352        _peakCount = 0;
 1353        _version++;
 1354    }
 355
 356    #endregion
 357
 358    #region Capacity Management
 359
 360    public void EnsureCapacity(int capacity)
 2361    {
 2362        capacity = SwiftHashTools.NextPowerOfTwo(capacity);
 2363        if (capacity > _innerArray.Length)
 2364            Resize(capacity);
 2365    }
 366
 367    private void Resize(int newSize)
 26368    {
 26369        int newCapacity = newSize <= DefaultCapacity ? DefaultCapacity : newSize;
 26370        int copyLength = Math.Min(_peakCount, _innerArray.Length);
 371
 26372        Entry[] newArray = new Entry[newCapacity];
 26373        if (copyLength > 0)
 26374            Array.Copy(_innerArray, 0, newArray, 0, copyLength);
 26375        _innerArray = newArray;
 376
 26377        _version++;
 26378    }
 379
 380    /// <summary>
 381    /// Reduces unused tail capacity while preserving stable handles for all currently allocated entries.
 382    /// </summary>
 383    public void TrimExcessCapacity()
 2384    {
 2385        int newPeak = GetLivePeakCount();
 2386        int newCapacity = newPeak <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(newPeak);
 387
 2388        Entry[] newArray = new Entry[newCapacity];
 2389        if (newPeak > 0)
 2390            Array.Copy(_innerArray, 0, newArray, 0, newPeak);
 391
 2392        _innerArray = newArray;
 2393        _peakCount = newPeak;
 2394        RebuildFreeIndices();
 395
 2396        _version++;
 2397    }
 398
 399    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 400    private int GetLivePeakCount()
 2401    {
 4402        for (int i = _peakCount - 1; i >= 0; i--)
 2403            if (_innerArray[i].IsUsed)
 2404                return i + 1;
 405
 0406        return 0;
 2407    }
 408
 409    private void RebuildFreeIndices()
 2410    {
 2411        _freeIndices = new SwiftIntStack(Math.Max(SwiftIntStack.DefaultCapacity, _peakCount - _count));
 412
 74413        for (int i = 0; i < _peakCount; i++)
 35414        {
 35415            if (!_innerArray[i].IsUsed)
 30416                _freeIndices.Push(i);
 35417        }
 2418    }
 419
 420    #endregion
 421
 422    #region Utility Methods
 423
 424    /// <summary>
 425    /// Attempts to get the value at the specified arrayIndex.
 426    /// </summary>
 427    /// <param name="key">The arrayIndex of the item to get.</param>
 428    /// <param name="value">When this method returns, contains the value associated with the specified arrayIndex, if th
 429    /// <returns><c>true</c> if the bucket contains an element at the specified arrayIndex; otherwise, <c>false</c>.</re
 430    public bool TryGetValue(int key, out T value)
 2431    {
 2432        if (!IsAllocated(key))
 1433        {
 1434            value = default;
 1435            return false;
 436        }
 437
 1438        value = _innerArray[key].Value;
 1439        return true;
 2440    }
 441
 442    /// <summary>
 443    /// Determines whether the bucket contains a specific value.
 444    /// </summary>
 445    /// <param name="item">The object to locate in the bucket.</param>
 446    /// <returns><c>true</c> if item is found; otherwise, <c>false</c>.</returns>
 447    /// <remarks>
 448    /// This method performs a linear search and has a time complexity of O(n).
 449    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 450    /// </remarks>
 451    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 5452    public bool Contains(T item) => IndexOf(item) != -1;
 453
 454    /// <summary>
 455    /// Determines whether the <see cref="SwiftBucket{T}"/> contains an element that matches the conditions defined by t
 456    /// </summary>
 457    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 458    /// <returns><c>true</c> if the <see cref="SwiftBucket{T}"/> contains one or more elements that match the specified 
 459    public bool Exists(Predicate<T> match)
 1460    {
 1461        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 462
 1463        uint count = 0;
 464
 4465        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 2466        {
 2467            if (_innerArray[i].IsUsed)
 2468            {
 2469                if (match(_innerArray[i].Value))
 1470                    return true;
 471
 1472                count++;
 1473            }
 1474        }
 475
 0476        return false;
 1477    }
 478
 479    /// <summary>
 480    /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first ma
 481    /// </summary>
 482    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 483    /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, 
 484    public T Find(Predicate<T> match)
 2485    {
 2486        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 487
 2488        uint count = 0;
 489
 10490        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 4491        {
 4492            if (_innerArray[i].IsUsed)
 4493            {
 4494                T item = _innerArray[i].Value;
 4495                if (match(item))
 1496                    return item;
 497
 3498                count++;
 3499            }
 3500        }
 501
 1502        return default;
 2503    }
 504
 505    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50232506    public bool IsAllocated(int index) => !((uint)index >= (uint)_innerArray.Length) && _innerArray[index].IsUsed;
 507
 508    /// <summary>
 509    /// Searches for the specified object and returns the zero-based arrayIndex of the first occurrence within the bucke
 510    /// </summary>
 511    /// <param name="item">The object to locate in the bucket.</param>
 512    /// <returns>
 513    /// The zero-based arrayIndex of the first occurrence of <paramref name="item"/> within the bucket, if found; otherw
 514    /// </returns>
 515    /// <remarks>
 516    /// This method performs a linear search and has a time complexity of O(n).
 517    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 518    /// </remarks>
 519    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 520    public int IndexOf(T item)
 8521    {
 8522        uint count = 0;
 523
 8524        if (item == null)
 0525        {
 0526            for (int i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 0527            {
 0528                if (_innerArray[i].IsUsed)
 0529                {
 0530                    if (_innerArray[i].Value == null)
 0531                        return i;
 0532                    count++;
 0533                }
 0534            }
 535
 0536            return -1;
 537        }
 538
 40539        for (int j = 0; j < (uint)_peakCount && count < (uint)_count; j++)
 17540        {
 17541            if (_innerArray[j].IsUsed)
 12542            {
 12543                if (EqualityComparer<T>.Default.Equals(_innerArray[j].Value, item))
 5544                    return j;
 7545                count++;
 7546            }
 12547        }
 3548        return -1;
 8549    }
 550
 551    /// <summary>
 552    /// Copies the elements of the bucket to an <see cref="Array"/>, starting at a particular Array arrayIndex.
 553    /// </summary>
 554    /// <param name="array">The one-dimensional Array that is the destination of the elements copied from bucket.</param
 555    /// <param name="arrayIndex">The zero-based arrayIndex in array at which copying begins.</param>
 556    public void CopyTo(T[] array, int arrayIndex)
 1557    {
 1558        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 1559        if ((uint)arrayIndex > array.Length) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
 1560        if (array.Length - arrayIndex < _count) throw new InvalidOperationException("The array is not large enough to ho
 561
 1562        uint count = 0;
 8563        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 3564        {
 3565            if (_innerArray[i].IsUsed)
 3566            {
 3567                array[arrayIndex++] = _innerArray[i].Value;
 3568                count++;
 3569            }
 3570        }
 1571    }
 572
 573    void ICollection.CopyTo(Array array, int arrayIndex)
 1574    {
 1575        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 1576        if ((uint)array.Rank != 1) throw new ArgumentException("Array must be single dimensional.");
 1577        if ((uint)array.GetLowerBound(0) != 0) throw new ArgumentException("Array must have zero-based indexing.");
 1578        if ((uint)arrayIndex > array.Length) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
 1579        if (array.Length - arrayIndex < _count) throw new InvalidOperationException("The array is not large enough to ho
 580
 581        try
 1582        {
 1583            uint count = 0;
 6584            for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 2585            {
 2586                if (_innerArray[i].IsUsed)
 2587                {
 2588                    array.SetValue(_innerArray[i].Value, arrayIndex++);
 2589                    count++;
 2590                }
 2591            }
 1592        }
 0593        catch (ArrayTypeMismatchException)
 0594        {
 0595            throw new ArgumentException("Invalid array type.");
 596        }
 1597    }
 598
 599    public void CloneTo(ICollection<T> output)
 1600    {
 1601        output.Clear();
 1602        uint count = 0;
 6603        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 2604        {
 2605            if (_innerArray[i].IsUsed)
 2606            {
 2607                output.Add(_innerArray[i].Value);
 2608                count++;
 2609            }
 2610        }
 1611    }
 612
 613    #endregion
 614
 615    #region Enumerator
 616
 617    /// <summary>
 618    /// Returns an enumerator that iterates through the <see cref="SwiftBucket{T}"/>.
 619    /// </summary>
 620    /// <returns>An enumerator for the bucket.</returns>
 6621    public SwiftBucketEnumerator GetEnumerator() => new SwiftBucketEnumerator(this);
 2622    IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
 1623    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 624
 625    public struct SwiftBucketEnumerator : IEnumerator<T>, IDisposable
 626    {
 627        private readonly SwiftBucket<T> _bucket;
 628        private readonly Entry[] _entries;
 629        private readonly uint _version;
 630        private int _index;
 631        private T _current;
 632
 633        internal SwiftBucketEnumerator(SwiftBucket<T> bucket)
 6634        {
 6635            _bucket = bucket;
 6636            _entries = bucket._innerArray;
 6637            _version = bucket._version;
 6638            _index = -1;
 6639            _current = default;
 6640        }
 641
 410642        public T Current => _current;
 643
 644        object IEnumerator.Current
 645        {
 646            get
 1647            {
 1648                if (_index > (uint)_bucket._count) throw new InvalidOperationException("Bad enumeration");
 1649                return _current;
 1650            }
 651        }
 652
 653        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 654        public bool MoveNext()
 217655        {
 217656            if (_version != _bucket._version)
 1657                throw new InvalidOperationException("Enumerator modified outside of enumeration!");
 658
 216659            uint count = (uint)_bucket._peakCount;
 217660            while (++_index < count)
 212661            {
 212662                if (_entries[_index].IsUsed)
 211663                {
 211664                    _current = _entries[_index].Value;
 211665                    return true;
 666                }
 1667            }
 5668            return false;
 216669        }
 670
 671        public void Reset()
 1672        {
 1673            if (_version != _bucket._version)
 0674                throw new InvalidOperationException("Enumerator modified outside of enumeration!");
 675
 1676            _index = -1;
 1677            _current = default;
 1678        }
 679
 8680        public void Dispose() { }
 681    }
 682
 683    #endregion
 684}