< Summary

Information
Class: SwiftCollections.SwiftBucket<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftBucket.cs
Line coverage
98%
Covered lines: 235
Uncovered lines: 4
Coverable lines: 239
Total lines: 744
Line coverage: 98.3%
Branch coverage
92%
Covered branches: 119
Total branches: 128
Branch coverage: 92.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%11100%
set_Item(...)100%11100%
get_IsReadOnly()100%11100%
get_IsSynchronized()100%11100%
get_SyncRoot()100%22100%
get_State()100%44100%
set_State(...)62.5%88100%
RestoreAllocatedEntries(...)100%88100%
RestoreFreeIndices(...)100%44100%
NormalizePeakCount(...)100%44100%
Add(...)100%44100%
System.Collections.Generic.ICollection<T>.Add(...)100%11100%
InsertAt(...)100%66100%
TryRemove(...)100%22100%
System.Collections.Generic.ICollection<T>.Remove(...)100%11100%
TryRemoveAt(...)100%22100%
RemoveAt(...)100%11100%
Clear()75%44100%
EnsureCapacity(...)100%22100%
Resize(...)75%44100%
TrimExcessCapacity()100%44100%
GetLivePeakCount()50%4475%
RebuildFreeIndices()100%44100%
TryGetValue(...)100%22100%
Contains(...)100%11100%
Exists(...)100%88100%
Find(...)100%88100%
IsAllocated(...)100%22100%
IndexOf(...)88.88%181892.85%
CopyTo(...)100%66100%
System.Collections.ICollection.CopyTo(...)100%6687.5%
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()100%11100%
MoveNext()100%44100%
Reset()100%11100%
Dispose()100%11100%

File(s)

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

#LineLine coverage
 1using Chronicler;
 2using MemoryPack;
 3using System;
 4using System.Collections;
 5using System.Collections.Generic;
 6using System.Runtime.CompilerServices;
 7using System.Text.Json.Serialization;
 8
 9namespace SwiftCollections;
 10
 11/// <summary>
 12/// Represents a high-performance bucket collection that assigns and manages stable integer indices
 13/// for stored items. Provides O(1) insertion, removal, and lookup by internally generated index.
 14/// </summary>
 15/// <remarks>
 16/// Unlike <see cref="SwiftSparseMap{T}"/>, which requires callers to provide the key used to store values,
 17/// <see cref="SwiftBucket{T}"/> internally generates and manages indices for each inserted item.
 18///
 19/// These indices remain stable for the lifetime of the item unless it is removed.
 20///
 21/// The container is optimized for scenarios requiring:
 22/// <list type="bullet">
 23///     <item>
 24///         <description>Stable handles or identifiers.</description>
 25///     </item>
 26///     <item>
 27///         <description>Fast addition and removal.</description>
 28///     </item>
 29///     <item>
 30///         <description>Dense storage and iteration performance.</description>
 31///     </item>
 32/// </list>
 33///
 34/// **Efficient Lookups Using Indices**:
 35/// When you add items to the bucket using the <see cref="Add"/> method, it returns an arrayIndex that you can store ext
 36/// You can then use this arrayIndex to access the item directly via the indexer, and check if it's still present using 
 37/// This approach allows for O(1) time complexity for lookups and existence checks, avoiding the need for O(n) searches 
 38///
 39/// **Note**: iteration over the collection does not follow any guaranteed order and depends on internal allocation.
 40/// </remarks>
 41/// <typeparam name="T">Specifies the type of elements in the bucket.</typeparam>
 42[Serializable]
 43[JsonConverter(typeof(StateJsonConverterFactory))]
 44[MemoryPackable]
 45public sealed partial class SwiftBucket<T> : IStateBacked<SwiftBucketState<T>>, ISwiftCloneable<T>, IEnumerable<T>, ICol
 46{
 47    #region Constants
 48
 49    /// <summary>
 50    /// Represents the default initial capacity used when no specific capacity is provided.
 51    /// </summary>
 52    public const int DefaultCapacity = 8;
 53
 54    #endregion
 55
 56    #region Fields
 57
 58    private Entry[] _innerArray;
 59
 60    private int _count;
 61
 62    private int _peakCount;
 63
 64    private SwiftIntStack _freeIndices;
 65
 66    [NonSerialized]
 67    private uint _version;
 68
 69    [NonSerialized]
 70    private object? _syncRoot;
 71
 72    #endregion
 73
 74    #region Nested Types
 75
 76    [Serializable]
 77    private struct Entry
 78    {
 79        public T Value;
 80        public bool IsUsed;
 81    }
 82
 83    #endregion
 84
 85    #region Constructors
 86
 87    /// <summary>
 88    /// Initializes a new instance of the <see cref="SwiftBucket{T}"/> class.
 89    /// </summary>
 6090    public SwiftBucket() : this(DefaultCapacity) { }
 91
 92    /// <summary>
 93    /// Initializes a new instance of the <see cref="SwiftBucket{T}"/> class with the specified capacity.
 94    /// </summary>
 95    /// <param name="capacity">The initial capacity of the bucket.</param>
 3696    public SwiftBucket(int capacity)
 97    {
 3698        capacity = capacity <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(capacity);
 3699        _innerArray = new Entry[capacity];
 36100        _freeIndices = new SwiftIntStack(SwiftIntStack.DefaultCapacity);
 36101    }
 102
 103    ///  <summary>
 104    ///  Initializes a new instance of the <see cref="SwiftBucket{T}"/> class with the specified <see cref="SwiftArraySt
 105    ///  </summary>
 106    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 107    [MemoryPackConstructor]
 6108    public SwiftBucket(SwiftBucketState<T> state)
 109    {
 6110        State = state;
 111
 5112        SwiftThrowHelper.ThrowIfNull(_innerArray, nameof(state.Items));
 5113        SwiftThrowHelper.ThrowIfNull(_freeIndices, nameof(state.FreeIndices));
 5114    }
 115
 116    #endregion
 117
 118    #region Properties
 119
 120    /// <summary>
 121    /// Gets the number of elements contained in the <see cref="SwiftBucket{T}"/>.
 122    /// </summary>
 123    [JsonIgnore]
 124    [MemoryPackIgnore]
 117125    public int Count => _count;
 126
 127    /// <summary>
 128    /// Gets the highest value recorded for the count during the lifetime of the object.
 129    /// </summary>
 130    [JsonIgnore]
 131    [MemoryPackIgnore]
 7132    public int PeakCount => _peakCount;
 133
 134    /// <summary>
 135    /// Gets the total capacity of the <see cref="SwiftBucket{T}"/>.
 136    /// </summary>
 137    [JsonIgnore]
 138    [MemoryPackIgnore]
 10139    public int Capacity => _innerArray.Length;
 140
 141    /// <summary>
 142    /// Gets or sets the element at the specified arrayIndex.
 143    /// Throws <see cref="InvalidOperationException"/> if the arrayIndex is invalid or unallocated.
 144    /// </summary>
 145    /// <param name="index">The zero-based arrayIndex of the element to get or set.</param>
 146    [JsonIgnore]
 147    [MemoryPackIgnore]
 148    public T this[int index]
 149    {
 150        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 151        get
 152        {
 226153            SwiftThrowHelper.ThrowIfTrue(!IsAllocated(index), nameof(index), message: "Index is out of range or unalloca
 223154            return _innerArray[index].Value;
 155        }
 156        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 157        set
 158        {
 1159            SwiftThrowHelper.ThrowIfTrue(!IsAllocated(index), nameof(index), message: "Index is out of range or unalloca
 1160            _innerArray[index].Value = value;
 1161            _version++;
 1162        }
 163    }
 164
 165    ///<inheritdoc/>
 166    [JsonIgnore]
 167    [MemoryPackIgnore]
 1168    public bool IsReadOnly => false;
 169
 170    ///<inheritdoc/>
 171    [JsonIgnore]
 172    [MemoryPackIgnore]
 1173    public bool IsSynchronized => false;
 174
 175    ///<inheritdoc/>
 176    [JsonIgnore]
 177    [MemoryPackIgnore]
 1178    public object SyncRoot => _syncRoot ??= new object();
 179
 180    /// <summary>
 181    /// Gets or sets the current state of the bucket, including all items, allocation status, and free indices.
 182    /// </summary>
 183    /// <remarks>
 184    /// Use this property to capture or restore the complete state of the bucket, such as for serialization or checkpoin
 185    /// Setting this property replaces the entire internal state, including items and allocation metadata.
 186    /// </remarks>
 187    [JsonInclude]
 188    [MemoryPackInclude]
 189    public SwiftBucketState<T> State
 190    {
 191        get
 192        {
 2193            int length = _innerArray.Length;
 194
 2195            var items = new T[length];
 2196            var allocated = new bool[length];
 197
 516198            for (int i = 0; i < length; i++)
 199            {
 256200                if (_innerArray[i].IsUsed)
 201                {
 200202                    items[i] = _innerArray[i].Value;
 200203                    allocated[i] = true;
 204                }
 205            }
 206
 2207            int[] free = new int[_freeIndices.Count];
 2208            Array.Copy(_freeIndices.Array, free, _freeIndices.Count);
 209
 2210            return new SwiftBucketState<T>(
 2211                items,
 2212                allocated,
 2213                free,
 2214                _peakCount
 2215            );
 216        }
 217        internal set
 218        {
 6219            var items = value.Items ?? Array.Empty<T>();
 6220            var allocated = value.Allocated ?? Array.Empty<bool>();
 6221            var freeIndices = value.FreeIndices ?? Array.Empty<int>();
 222
 6223            int sourceLength = Math.Max(items.Length, allocated.Length);
 6224            int capacity = sourceLength < DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(sourceLength
 225
 6226            _innerArray = new Entry[capacity];
 6227            _freeIndices = new SwiftIntStack(Math.Max(SwiftIntStack.DefaultCapacity, freeIndices.Length));
 228
 6229            _count = 0;
 6230            int maxReferencedIndex = RestoreAllocatedEntries(items, allocated, sourceLength);
 6231            maxReferencedIndex = RestoreFreeIndices(freeIndices, capacity, maxReferencedIndex);
 5232            _peakCount = NormalizePeakCount(value.PeakCount, maxReferencedIndex, capacity);
 5233            _version = 0;
 5234        }
 235    }
 236
 237    private int RestoreAllocatedEntries(T[] items, bool[] allocated, int sourceLength)
 238    {
 6239        int maxReferencedIndex = -1;
 536240        for (int i = 0; i < sourceLength; i++)
 241        {
 262242            if (allocated.Length <= i || !allocated[i])
 243                continue;
 244
 205245            if (items.Length > i)
 205246                _innerArray[i].Value = items[i];
 247
 205248            _innerArray[i].IsUsed = true;
 205249            _count++;
 205250            maxReferencedIndex = i;
 251        }
 252
 6253        return maxReferencedIndex;
 254    }
 255
 256    private int RestoreFreeIndices(int[] freeIndices, int capacity, int maxReferencedIndex)
 257    {
 15258        foreach (var index in freeIndices)
 259        {
 2260            SwiftThrowHelper.ThrowIfTrue((uint)index >= (uint)capacity, message: "Free index is out of range.");
 261
 1262            _freeIndices.Push(index);
 1263            if (index > maxReferencedIndex)
 1264                maxReferencedIndex = index;
 265        }
 266
 5267        return maxReferencedIndex;
 268    }
 269
 270    private static int NormalizePeakCount(int peakCount, int maxReferencedIndex, int capacity)
 271    {
 5272        if (peakCount < 0)
 1273            peakCount = 0;
 274
 5275        int normalizedPeak = Math.Max(peakCount, maxReferencedIndex + 1);
 5276        return normalizedPeak > capacity ? capacity : normalizedPeak;
 277    }
 278
 279    #endregion
 280
 281    #region Collection Management
 282
 283    /// <summary>
 284    /// Adds an item to the bucket and returns its arrayIndex.
 285    /// </summary>
 286    /// <param name="item">The item to add.</param>
 287    /// <returns>The arrayIndex where the item was added.</returns>
 288    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 289    public int Add(T item)
 290    {
 291        int index;
 100281292        if ((uint)_freeIndices.Count == 0)
 293        {
 100279294            index = _peakCount++;
 100279295            if ((uint)index >= (uint)_innerArray.Length)
 24296                Resize(_innerArray.Length * 2);
 297        }
 2298        else index = _freeIndices.Pop();
 299
 100281300        _innerArray[index].Value = item;
 100281301        _innerArray[index].IsUsed = true;
 100281302        _count++;
 100281303        _version++;
 100281304        return index;
 305    }
 306
 1307    void ICollection<T>.Add(T item) => Add(item);
 308
 309    /// <summary>
 310    /// Inserts an item at the specified arrayIndex.
 311    /// If an item already exists at that arrayIndex, it will be replaced.
 312    /// </summary>
 313    /// <param name="index">The arrayIndex at which to insert the item.</param>
 314    /// <param name="item">The item to insert.</param>
 315    public void InsertAt(int index, T item)
 316    {
 7317        SwiftThrowHelper.ThrowIfNegative(index, nameof(index));
 7318        if ((uint)index >= (uint)_innerArray.Length)
 1319            Resize(SwiftHashTools.NextPowerOfTwo(index + 1));
 7320        if (!_innerArray[index].IsUsed)
 321        {
 6322            _count++;
 6323            if ((uint)index >= (uint)_peakCount)
 6324                _peakCount = index + 1;
 325        }
 7326        _innerArray[index].Value = item;
 7327        _innerArray[index].IsUsed = true;
 7328        _version++;
 7329    }
 330
 331    /// <summary>
 332    /// Removes the first occurrence of a specific object from the bucket.
 333    /// </summary>
 334    /// <param name="item">The object to remove.</param>
 335    /// <returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
 336    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 337    public bool TryRemove(T item)
 338    {
 3339        int index = IndexOf(item);
 4340        if (index < 0) return false;
 2341        RemoveAt(index);
 2342        return true;
 343    }
 344
 1345    bool ICollection<T>.Remove(T item) => TryRemove(item);
 346
 347    /// <summary>
 348    /// Removes the item at the specified arrayIndex if it has been allocated.
 349    /// </summary>
 350    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 351    public bool TryRemoveAt(int index)
 352    {
 50004353        if (IsAllocated(index))
 354        {
 50003355            RemoveAt(index);
 50003356            return true;
 357        }
 1358        return false;
 359    }
 360
 361    /// <summary>
 362    /// Removes the item at the specified arrayIndex.
 363    /// </summary>
 364    /// <param name="index">The arrayIndex of the item to remove.</param>
 365    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 366    public void RemoveAt(int index)
 367    {
 50005368        _innerArray[index] = default;
 50005369        _count--;
 50005370        _freeIndices.Push(index);
 50005371        _version++;
 50005372    }
 373
 374    /// <summary>
 375    /// Removes all items from the bucket.
 376    /// </summary>
 377    public void Clear()
 378    {
 1379        if ((uint)_count == 0) return;
 8380        for (int i = 0; i < _peakCount; i++)
 3381            _innerArray[i] = default;
 1382        _freeIndices.Reset();
 1383        _count = 0;
 1384        _peakCount = 0;
 1385        _version++;
 1386    }
 387
 388    #endregion
 389
 390    #region Capacity Management
 391
 392    /// <summary>
 393    /// Ensures that the internal storage has at least the specified capacity, expanding it if necessary.
 394    /// </summary>
 395    /// <remarks>
 396    /// If the current capacity is less than the specified value, the internal storage is increased to
 397    /// the next power of two greater than or equal to the requested capacity.
 398    /// No action is taken if the current capacity is sufficient.
 399    /// </remarks>
 400    /// <param name="capacity">The minimum number of elements that the internal storage should be able to hold. Must be 
 401    public void EnsureCapacity(int capacity)
 402    {
 2403        capacity = SwiftHashTools.NextPowerOfTwo(capacity);
 2404        if (capacity > _innerArray.Length)
 2405            Resize(capacity);
 2406    }
 407
 408    private void Resize(int newSize)
 409    {
 27410        int newCapacity = newSize <= DefaultCapacity ? DefaultCapacity : newSize;
 27411        int copyLength = Math.Min(_peakCount, _innerArray.Length);
 412
 27413        Entry[] newArray = new Entry[newCapacity];
 27414        if (copyLength > 0)
 27415            Array.Copy(_innerArray, 0, newArray, 0, copyLength);
 27416        _innerArray = newArray;
 417
 27418        _version++;
 27419    }
 420
 421    /// <summary>
 422    /// Reduces unused tail capacity while preserving stable handles for all currently allocated entries.
 423    /// </summary>
 424    public void TrimExcessCapacity()
 425    {
 2426        int newPeak = GetLivePeakCount();
 2427        int newCapacity = newPeak <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(newPeak);
 428
 2429        Entry[] newArray = new Entry[newCapacity];
 2430        if (newPeak > 0)
 2431            Array.Copy(_innerArray, 0, newArray, 0, newPeak);
 432
 2433        _innerArray = newArray;
 2434        _peakCount = newPeak;
 2435        RebuildFreeIndices();
 436
 2437        _version++;
 2438    }
 439
 440    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 441    private int GetLivePeakCount()
 442    {
 4443        for (int i = _peakCount - 1; i >= 0; i--)
 2444            if (_innerArray[i].IsUsed)
 2445                return i + 1;
 446
 0447        return 0;
 448    }
 449
 450    private void RebuildFreeIndices()
 451    {
 2452        _freeIndices = new SwiftIntStack(Math.Max(SwiftIntStack.DefaultCapacity, _peakCount - _count));
 453
 74454        for (int i = 0; i < _peakCount; i++)
 455        {
 35456            if (!_innerArray[i].IsUsed)
 30457                _freeIndices.Push(i);
 458        }
 2459    }
 460
 461    #endregion
 462
 463    #region Utility Methods
 464
 465    /// <summary>
 466    /// Attempts to get the value at the specified arrayIndex.
 467    /// </summary>
 468    /// <param name="key">The arrayIndex of the item to get.</param>
 469    /// <param name="value">When this method returns, contains the value associated with the specified arrayIndex, if th
 470    /// <returns><c>true</c> if the bucket contains an element at the specified arrayIndex; otherwise, <c>false</c>.</re
 471    public bool TryGetValue(int key, out T value)
 472    {
 3473        if (!IsAllocated(key))
 474        {
 2475            value = default!;
 2476            return false;
 477        }
 478
 1479        value = _innerArray[key].Value;
 1480        return true;
 481    }
 482
 483    /// <summary>
 484    /// Determines whether the bucket contains a specific value.
 485    /// </summary>
 486    /// <param name="item">The object to locate in the bucket.</param>
 487    /// <returns><c>true</c> if item is found; otherwise, <c>false</c>.</returns>
 488    /// <remarks>
 489    /// This method performs a linear search and has a time complexity of O(n).
 490    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 491    /// </remarks>
 492    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 6493    public bool Contains(T item) => IndexOf(item) != -1;
 494
 495    /// <summary>
 496    /// Determines whether the <see cref="SwiftBucket{T}"/> contains an element that matches the conditions defined by t
 497    /// </summary>
 498    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 499    /// <returns><c>true</c> if the <see cref="SwiftBucket{T}"/> contains one or more elements that match the specified 
 500    public bool Exists(Predicate<T> match)
 501    {
 2502        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 503
 2504        uint count = 0;
 505
 12506        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 507        {
 5508            if (_innerArray[i].IsUsed)
 509            {
 5510                if (match(_innerArray[i].Value))
 1511                    return true;
 512
 4513                count++;
 514            }
 515        }
 516
 1517        return false;
 518    }
 519
 520    /// <summary>
 521    /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first ma
 522    /// </summary>
 523    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 524    /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, 
 525    public T Find(Predicate<T> match)
 526    {
 2527        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 528
 2529        uint count = 0;
 530
 10531        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 532        {
 4533            if (_innerArray[i].IsUsed)
 534            {
 4535                T item = _innerArray[i].Value;
 4536                if (match(item))
 1537                    return item;
 538
 3539                count++;
 540            }
 541        }
 542
 1543        return default!;
 544    }
 545
 546    /// <summary>
 547    /// Determines whether the element at the specified index is currently allocated.
 548    /// </summary>
 549    /// <param name="index">The zero-based index of the element to check. Must be greater than or equal to 0 and less th
 550    /// <returns>true if the element at the specified index is allocated; otherwise, false.</returns>
 551    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50236552    public bool IsAllocated(int index) => !((uint)index >= (uint)_innerArray.Length) && _innerArray[index].IsUsed;
 553
 554    /// <summary>
 555    /// Searches for the specified object and returns the zero-based arrayIndex of the first occurrence within the bucke
 556    /// </summary>
 557    /// <param name="item">The object to locate in the bucket.</param>
 558    /// <returns>
 559    /// The zero-based arrayIndex of the first occurrence of <paramref name="item"/> within the bucket, if found; otherw
 560    /// </returns>
 561    /// <remarks>
 562    /// This method performs a linear search and has a time complexity of O(n).
 563    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 564    /// </remarks>
 565    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 566    public int IndexOf(T item)
 567    {
 10568        uint count = 0;
 569
 10570        if (item == null)
 571        {
 8572            for (int i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 573            {
 4574                if (_innerArray[i].IsUsed)
 575                {
 4576                    if (_innerArray[i].Value == null)
 2577                        return i;
 2578                    count++;
 579                }
 580            }
 581
 0582            return -1;
 583        }
 584
 40585        for (int j = 0; j < (uint)_peakCount && count < (uint)_count; j++)
 586        {
 17587            if (_innerArray[j].IsUsed)
 588            {
 12589                if (EqualityComparer<T>.Default.Equals(_innerArray[j].Value, item))
 5590                    return j;
 7591                count++;
 592            }
 593        }
 3594        return -1;
 595    }
 596
 597    /// <summary>
 598    /// Copies the elements of the bucket to an <see cref="Array"/>, starting at a particular Array arrayIndex.
 599    /// </summary>
 600    /// <param name="array">The one-dimensional Array that is the destination of the elements copied from bucket.</param
 601    /// <param name="arrayIndex">The zero-based arrayIndex in array at which copying begins.</param>
 602    public void CopyTo(T[] array, int arrayIndex)
 603    {
 1604        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 1605        SwiftThrowHelper.ThrowIfArrayIndexInvalid(arrayIndex, array.Length, message: "Array index is out of range.");
 1606        SwiftThrowHelper.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "The array is not large enou
 607
 1608        uint count = 0;
 8609        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 610        {
 3611            if (_innerArray[i].IsUsed)
 612            {
 3613                array[arrayIndex++] = _innerArray[i].Value;
 3614                count++;
 615            }
 616        }
 1617    }
 618
 619    void ICollection.CopyTo(Array array, int arrayIndex)
 620    {
 6621        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 6622        SwiftThrowHelper.ThrowIfArgument((uint)array.Rank != 1, nameof(array), "Array must be single dimensional.");
 5623        SwiftThrowHelper.ThrowIfArgument((uint)array.GetLowerBound(0) != 0, nameof(array), "Array must have zero-based i
 4624        SwiftThrowHelper.ThrowIfArrayIndexInvalid(arrayIndex, array.Length, message: "Array index is out of range.");
 3625        SwiftThrowHelper.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "The array is not large enou
 626
 627        try
 628        {
 2629            uint count = 0;
 8630            for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 631            {
 3632                if (_innerArray[i].IsUsed)
 633                {
 3634                    array.SetValue(_innerArray[i].Value, arrayIndex++);
 2635                    count++;
 636                }
 637            }
 1638        }
 0639        catch (ArrayTypeMismatchException)
 640        {
 0641            throw new ArgumentException("Invalid array type.");
 642        }
 1643        catch (InvalidCastException)
 644        {
 1645            throw new ArgumentException("Invalid array type.");
 646        }
 1647    }
 648
 649    /// <inheritdoc/>
 650    public void CloneTo(ICollection<T> output)
 651    {
 1652        output.Clear();
 1653        uint count = 0;
 6654        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 655        {
 2656            if (_innerArray[i].IsUsed)
 657            {
 2658                output.Add(_innerArray[i].Value);
 2659                count++;
 660            }
 661        }
 1662    }
 663
 664    #endregion
 665
 666    #region Enumerator
 667
 668    /// <summary>
 669    /// Returns an enumerator that iterates through the <see cref="SwiftBucket{T}"/>.
 670    /// </summary>
 671    /// <returns>An enumerator for the bucket.</returns>
 6672    public SwiftBucketEnumerator GetEnumerator() => new(this);
 2673    IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
 1674    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 675
 676    /// <summary>
 677    /// Enumerates the elements of a <see cref="SwiftBucket{T}"/> collection.
 678    /// </summary>
 679    /// <remarks>
 680    /// The enumerator provides read-only, forward-only iteration over the elements in the <see cref="SwiftBucket{T}"/>.
 681    /// The enumerator is invalidated if the collection is modified after the enumerator is created.
 682    /// </remarks>
 683    public struct SwiftBucketEnumerator : IEnumerator<T>, IDisposable
 684    {
 685        private readonly SwiftBucket<T> _bucket;
 686        private readonly Entry[] _entries;
 687        private readonly uint _version;
 688        private int _index;
 689        private T _current;
 690
 691        internal SwiftBucketEnumerator(SwiftBucket<T> bucket)
 692        {
 6693            _bucket = bucket;
 6694            _entries = bucket._innerArray;
 6695            _version = bucket._version;
 6696            _index = -1;
 6697            _current = default!;
 6698        }
 699
 700        /// <inheritdoc/>
 410701        public T Current => _current;
 702
 703        object IEnumerator.Current
 704        {
 705            get
 706            {
 1707                SwiftThrowHelper.ThrowIfTrue(_index > (uint)_bucket._count, message: "Enumerator is before the first ele
 1708                return _current!;
 709            }
 710        }
 711
 712        /// <inheritdoc/>
 713        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 714        public bool MoveNext()
 715        {
 217716            SwiftThrowHelper.ThrowIfTrue(_version != _bucket._version, message: "Enumerator modified outside of enumerat
 717
 216718            uint count = (uint)_bucket._peakCount;
 217719            while (++_index < count)
 720            {
 212721                if (_entries[_index].IsUsed)
 722                {
 211723                    _current = _entries[_index].Value;
 211724                    return true;
 725                }
 726            }
 5727            return false;
 728        }
 729
 730        /// <inheritdoc/>
 731        public void Reset()
 732        {
 1733            SwiftThrowHelper.ThrowIfTrue(_version != _bucket._version, message: "Enumerator modified outside of enumerat
 734
 1735            _index = -1;
 1736            _current = default!;
 1737        }
 738
 739        /// <inheritdoc/>
 4740        public void Dispose() => _index = -1;
 741    }
 742
 743    #endregion
 744}