< Summary

Information
Class: SwiftCollections.SwiftBucket<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftBucket.cs
Line coverage
100%
Covered lines: 239
Uncovered lines: 0
Coverable lines: 239
Total lines: 752
Line coverage: 100%
Branch coverage
92%
Covered branches: 117
Total branches: 126
Branch coverage: 92.8%
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()50%22100%
get_State()100%44100%
set_State(...)100%88100%
RestoreAllocatedEntries(...)87.5%88100%
RestoreFreeIndices(...)75%44100%
NormalizePeakCount(...)100%44100%
Add(...)100%44100%
System.Collections.Generic.ICollection<T>.Add(...)100%11100%
InsertAt(...)83.33%66100%
TryRemove(...)100%22100%
System.Collections.Generic.ICollection<T>.Remove(...)100%11100%
TryRemoveAt(...)100%22100%
RemoveAt(...)100%11100%
Clear()100%44100%
EnsureCapacity(...)100%22100%
Resize(...)100%22100%
TrimExcessCapacity()100%44100%
GetLivePeakCount()100%44100%
RebuildFreeIndices()100%44100%
TryGetValue(...)100%22100%
Contains(...)100%11100%
Exists(...)87.5%88100%
Find(...)87.5%88100%
IsAllocated(...)100%22100%
IndexOf(...)94.44%1818100%
CopyTo(...)83.33%66100%
System.Collections.ICollection.CopyTo(...)100%66100%
CloneTo(...)83.33%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
 1//=======================================================================
 2// SwiftBucket.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using Chronicler;
 9using MemoryPack;
 10using SwiftCollections.Diagnostics;
 11using SwiftCollections.Utility;
 12using System;
 13using System.Collections;
 14using System.Collections.Generic;
 15using System.Runtime.CompilerServices;
 16using System.Text.Json.Serialization;
 17
 18namespace SwiftCollections;
 19
 20/// <summary>
 21/// Represents a high-performance bucket collection that assigns and manages stable integer indices
 22/// for stored items. Provides O(1) insertion, removal, and lookup by internally generated index.
 23/// </summary>
 24/// <remarks>
 25/// Unlike <see cref="SwiftSparseMap{T}"/>, which requires callers to provide the key used to store values,
 26/// <see cref="SwiftBucket{T}"/> internally generates and manages indices for each inserted item.
 27///
 28/// These indices remain stable for the lifetime of the item unless it is removed.
 29///
 30/// The container is optimized for scenarios requiring:
 31/// <list type="bullet">
 32///     <item>
 33///         <description>Stable handles or identifiers.</description>
 34///     </item>
 35///     <item>
 36///         <description>Fast addition and removal.</description>
 37///     </item>
 38///     <item>
 39///         <description>Dense storage and iteration performance.</description>
 40///     </item>
 41/// </list>
 42///
 43/// **Efficient Lookups Using Indices**:
 44/// When you add items to the bucket using the <see cref="Add"/> method, it returns an arrayIndex that you can store ext
 45/// You can then use this arrayIndex to access the item directly via the indexer, and check if it's still present using 
 46/// This approach allows for O(1) time complexity for lookups and existence checks, avoiding the need for O(n) searches 
 47///
 48/// **Note**: iteration over the collection does not follow any guaranteed order and depends on internal allocation.
 49/// </remarks>
 50/// <typeparam name="T">Specifies the type of elements in the bucket.</typeparam>
 51[Serializable]
 52[JsonConverter(typeof(StateJsonConverterFactory))]
 53[MemoryPackable]
 54public sealed partial class SwiftBucket<T> : IStateBacked<SwiftBucketState<T>>, ISwiftCloneable<T>, IEnumerable<T>, ICol
 55{
 56    #region Constants
 57
 58    /// <summary>
 59    /// Represents the default initial capacity used when no specific capacity is provided.
 60    /// </summary>
 61    public const int DefaultCapacity = 8;
 62
 63    #endregion
 64
 65    #region Fields
 66
 67    private Entry[] _innerArray;
 68
 69    private int _count;
 70
 71    private int _peakCount;
 72
 73    private SwiftIntStack _freeIndices;
 74
 75    [NonSerialized]
 76    private uint _version;
 77
 78    [NonSerialized]
 79    private object? _syncRoot;
 80
 81    #endregion
 82
 83    #region Nested Types
 84
 85    [Serializable]
 86    private struct Entry
 87    {
 88        public T Value;
 89        public bool IsUsed;
 90    }
 91
 92    #endregion
 93
 94    #region Constructors
 95
 96    /// <summary>
 97    /// Initializes a new instance of the <see cref="SwiftBucket{T}"/> class.
 98    /// </summary>
 7099    public SwiftBucket() : this(DefaultCapacity) { }
 100
 101    /// <summary>
 102    /// Initializes a new instance of the <see cref="SwiftBucket{T}"/> class with the specified capacity.
 103    /// </summary>
 104    /// <param name="capacity">The initial capacity of the bucket.</param>
 44105    public SwiftBucket(int capacity)
 106    {
 44107        capacity = capacity <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(capacity);
 44108        _innerArray = new Entry[capacity];
 44109        _freeIndices = new SwiftIntStack(capacity);
 44110    }
 111
 112    ///  <summary>
 113    ///  Initializes a new instance of the <see cref="SwiftBucket{T}"/> class with the specified <see cref="SwiftArraySt
 114    ///  </summary>
 115    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 116    [MemoryPackConstructor]
 8117    public SwiftBucket(SwiftBucketState<T> state)
 118    {
 8119        State = state;
 120
 7121        SwiftThrowHelper.ThrowIfNull(_innerArray, nameof(state.Items));
 7122        SwiftThrowHelper.ThrowIfNull(_freeIndices, nameof(state.FreeIndices));
 7123    }
 124
 125    #endregion
 126
 127    #region Properties
 128
 129    /// <summary>
 130    /// Gets the number of elements contained in the <see cref="SwiftBucket{T}"/>.
 131    /// </summary>
 132    [JsonIgnore]
 133    [MemoryPackIgnore]
 120134    public int Count => _count;
 135
 136    /// <summary>
 137    /// Gets the highest value recorded for the count during the lifetime of the object.
 138    /// </summary>
 139    [JsonIgnore]
 140    [MemoryPackIgnore]
 10141    public int PeakCount => _peakCount;
 142
 143    /// <summary>
 144    /// Gets the total capacity of the <see cref="SwiftBucket{T}"/>.
 145    /// </summary>
 146    [JsonIgnore]
 147    [MemoryPackIgnore]
 16148    public int Capacity => _innerArray.Length;
 149
 150    /// <summary>
 151    /// Gets or sets the element at the specified arrayIndex.
 152    /// Throws <see cref="InvalidOperationException"/> if the arrayIndex is invalid or unallocated.
 153    /// </summary>
 154    /// <param name="index">The zero-based arrayIndex of the element to get or set.</param>
 155    [JsonIgnore]
 156    [MemoryPackIgnore]
 157    public T this[int index]
 158    {
 159        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 160        get
 161        {
 228162            SwiftThrowHelper.ThrowIfTrue(!IsAllocated(index), nameof(index), message: "Index is out of range or unalloca
 225163            return _innerArray[index].Value;
 164        }
 165        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 166        set
 167        {
 1168            SwiftThrowHelper.ThrowIfTrue(!IsAllocated(index), nameof(index), message: "Index is out of range or unalloca
 1169            _innerArray[index].Value = value;
 1170            _version++;
 1171        }
 172    }
 173
 174    ///<inheritdoc/>
 175    [JsonIgnore]
 176    [MemoryPackIgnore]
 1177    public bool IsReadOnly => false;
 178
 179    ///<inheritdoc/>
 180    [JsonIgnore]
 181    [MemoryPackIgnore]
 1182    public bool IsSynchronized => false;
 183
 184    ///<inheritdoc/>
 185    [JsonIgnore]
 186    [MemoryPackIgnore]
 1187    public object SyncRoot => _syncRoot ??= new object();
 188
 189    /// <summary>
 190    /// Gets or sets the current state of the bucket, including all items, allocation status, and free indices.
 191    /// </summary>
 192    /// <remarks>
 193    /// Use this property to capture or restore the complete state of the bucket, such as for serialization or checkpoin
 194    /// Setting this property replaces the entire internal state, including items and allocation metadata.
 195    /// </remarks>
 196    [JsonInclude]
 197    [MemoryPackInclude]
 198    public SwiftBucketState<T> State
 199    {
 200        get
 201        {
 2202            int length = _innerArray.Length;
 203
 2204            var items = new T[length];
 2205            var allocated = new bool[length];
 206
 516207            for (int i = 0; i < length; i++)
 208            {
 256209                if (_innerArray[i].IsUsed)
 210                {
 200211                    items[i] = _innerArray[i].Value;
 200212                    allocated[i] = true;
 213                }
 214            }
 215
 2216            int[] free = new int[_freeIndices.Count];
 2217            Array.Copy(_freeIndices.Array, free, _freeIndices.Count);
 218
 2219            return new SwiftBucketState<T>(
 2220                items,
 2221                allocated,
 2222                free,
 2223                _peakCount
 2224            );
 225        }
 226        internal set
 227        {
 8228            var items = value.Items ?? Array.Empty<T>();
 8229            var allocated = value.Allocated ?? Array.Empty<bool>();
 8230            var freeIndices = value.FreeIndices ?? Array.Empty<int>();
 231
 8232            int sourceLength = Math.Max(items.Length, allocated.Length);
 8233            int capacity = sourceLength < DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(sourceLength
 234
 8235            _innerArray = new Entry[capacity];
 8236            _freeIndices = new SwiftIntStack(capacity);
 237
 8238            _count = 0;
 8239            int maxReferencedIndex = RestoreAllocatedEntries(items, allocated, sourceLength);
 8240            maxReferencedIndex = RestoreFreeIndices(freeIndices, capacity, maxReferencedIndex);
 7241            _peakCount = NormalizePeakCount(value.PeakCount, maxReferencedIndex, capacity);
 7242            _version = 0;
 7243        }
 244    }
 245
 246    private int RestoreAllocatedEntries(T[] items, bool[] allocated, int sourceLength)
 247    {
 8248        int maxReferencedIndex = -1;
 544249        for (int i = 0; i < sourceLength; i++)
 250        {
 264251            if (allocated.Length <= i || !allocated[i])
 252                continue;
 253
 206254            if (items.Length > i)
 206255                _innerArray[i].Value = items[i];
 256
 206257            _innerArray[i].IsUsed = true;
 206258            _count++;
 206259            maxReferencedIndex = i;
 260        }
 261
 8262        return maxReferencedIndex;
 263    }
 264
 265    private int RestoreFreeIndices(int[] freeIndices, int capacity, int maxReferencedIndex)
 266    {
 19267        foreach (var index in freeIndices)
 268        {
 2269            SwiftThrowHelper.ThrowIfTrue((uint)index >= (uint)capacity, message: "Free index is out of range.");
 270
 1271            _freeIndices.Push(index);
 1272            if (index > maxReferencedIndex)
 1273                maxReferencedIndex = index;
 274        }
 275
 7276        return maxReferencedIndex;
 277    }
 278
 279    private static int NormalizePeakCount(int peakCount, int maxReferencedIndex, int capacity)
 280    {
 7281        if (peakCount < 0)
 1282            peakCount = 0;
 283
 7284        int normalizedPeak = Math.Max(peakCount, maxReferencedIndex + 1);
 7285        return normalizedPeak > capacity ? capacity : normalizedPeak;
 286    }
 287
 288    #endregion
 289
 290    #region Collection Management
 291
 292    /// <summary>
 293    /// Adds an item to the bucket and returns its arrayIndex.
 294    /// </summary>
 295    /// <param name="item">The item to add.</param>
 296    /// <returns>The arrayIndex where the item was added.</returns>
 297    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 298    public int Add(T item)
 299    {
 300        int index;
 100543301        if ((uint)_freeIndices.Count == 0)
 302        {
 100413303            index = _peakCount++;
 100413304            if ((uint)index >= (uint)_innerArray.Length)
 24305                Resize(_innerArray.Length * 2);
 306        }
 130307        else index = _freeIndices.Pop();
 308
 100543309        _innerArray[index].Value = item;
 100543310        _innerArray[index].IsUsed = true;
 100543311        _count++;
 100543312        _version++;
 100543313        return index;
 314    }
 315
 1316    void ICollection<T>.Add(T item) => Add(item);
 317
 318    /// <summary>
 319    /// Inserts an item at the specified arrayIndex.
 320    /// If an item already exists at that arrayIndex, it will be replaced.
 321    /// </summary>
 322    /// <param name="index">The arrayIndex at which to insert the item.</param>
 323    /// <param name="item">The item to insert.</param>
 324    public void InsertAt(int index, T item)
 325    {
 7326        SwiftThrowHelper.ThrowIfNegative(index, nameof(index));
 7327        if ((uint)index >= (uint)_innerArray.Length)
 1328            Resize(SwiftHashTools.NextPowerOfTwo(index + 1));
 7329        if (!_innerArray[index].IsUsed)
 330        {
 6331            _count++;
 6332            if ((uint)index >= (uint)_peakCount)
 6333                _peakCount = index + 1;
 334        }
 7335        _innerArray[index].Value = item;
 7336        _innerArray[index].IsUsed = true;
 7337        _version++;
 7338    }
 339
 340    /// <summary>
 341    /// Removes the first occurrence of a specific object from the bucket.
 342    /// </summary>
 343    /// <param name="item">The object to remove.</param>
 344    /// <returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
 345    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 346    public bool TryRemove(T item)
 347    {
 3348        int index = IndexOf(item);
 4349        if (index < 0) return false;
 2350        RemoveAt(index);
 2351        return true;
 352    }
 353
 1354    bool ICollection<T>.Remove(T item) => TryRemove(item);
 355
 356    /// <summary>
 357    /// Removes the item at the specified arrayIndex if it has been allocated.
 358    /// </summary>
 359    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 360    public bool TryRemoveAt(int index)
 361    {
 50260362        if (IsAllocated(index))
 363        {
 50259364            RemoveAt(index);
 50259365            return true;
 366        }
 1367        return false;
 368    }
 369
 370    /// <summary>
 371    /// Removes the item at the specified arrayIndex.
 372    /// </summary>
 373    /// <param name="index">The arrayIndex of the item to remove.</param>
 374    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 375    public void RemoveAt(int index)
 376    {
 50262377        _innerArray[index] = default;
 50262378        _count--;
 50262379        _freeIndices.Push(index);
 50262380        _version++;
 50262381    }
 382
 383    /// <summary>
 384    /// Removes all items from the bucket.
 385    /// </summary>
 386    public void Clear()
 387    {
 3388        if ((uint)_count == 0) return;
 8389        for (int i = 0; i < _peakCount; i++)
 3390            _innerArray[i] = default;
 1391        _freeIndices.Reset();
 1392        _count = 0;
 1393        _peakCount = 0;
 1394        _version++;
 1395    }
 396
 397    #endregion
 398
 399    #region Capacity Management
 400
 401    /// <summary>
 402    /// Ensures that the internal storage has at least the specified capacity, expanding it if necessary.
 403    /// </summary>
 404    /// <remarks>
 405    /// If the current capacity is less than the specified value, the internal storage is increased to
 406    /// the next power of two greater than or equal to the requested capacity.
 407    /// No action is taken if the current capacity is sufficient.
 408    /// </remarks>
 409    /// <param name="capacity">The minimum number of elements that the internal storage should be able to hold. Must be 
 410    public void EnsureCapacity(int capacity)
 411    {
 4412        capacity = SwiftHashTools.NextPowerOfTwo(capacity);
 4413        if (capacity > _innerArray.Length)
 3414            Resize(capacity);
 415        else
 1416            _freeIndices.EnsureCapacity(capacity);
 1417    }
 418
 419    private void Resize(int newSize)
 420    {
 28421        int newCapacity = newSize;
 28422        int copyLength = Math.Min(_peakCount, _innerArray.Length);
 423
 28424        Entry[] newArray = new Entry[newCapacity];
 28425        if (copyLength > 0)
 27426            Array.Copy(_innerArray, 0, newArray, 0, copyLength);
 28427        _innerArray = newArray;
 28428        _freeIndices.EnsureCapacity(newCapacity);
 429
 28430        _version++;
 28431    }
 432
 433    /// <summary>
 434    /// Reduces unused tail capacity while preserving stable handles for all currently allocated entries.
 435    /// </summary>
 436    public void TrimExcessCapacity()
 437    {
 4438        int newPeak = GetLivePeakCount();
 4439        int newCapacity = newPeak <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(newPeak);
 440
 4441        Entry[] newArray = new Entry[newCapacity];
 4442        if (newPeak > 0)
 2443            Array.Copy(_innerArray, 0, newArray, 0, newPeak);
 444
 4445        _innerArray = newArray;
 4446        _peakCount = newPeak;
 4447        RebuildFreeIndices();
 448
 4449        _version++;
 4450    }
 451
 452    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 453    private int GetLivePeakCount()
 454    {
 10455        for (int i = _peakCount - 1; i >= 0; i--)
 3456            if (_innerArray[i].IsUsed)
 2457                return i + 1;
 458
 2459        return 0;
 460    }
 461
 462    private void RebuildFreeIndices()
 463    {
 4464        _freeIndices = new SwiftIntStack(_innerArray.Length);
 465
 78466        for (int i = 0; i < _peakCount; i++)
 467        {
 35468            if (!_innerArray[i].IsUsed)
 30469                _freeIndices.Push(i);
 470        }
 4471    }
 472
 473    #endregion
 474
 475    #region Utility Methods
 476
 477    /// <summary>
 478    /// Attempts to get the value at the specified arrayIndex.
 479    /// </summary>
 480    /// <param name="key">The arrayIndex of the item to get.</param>
 481    /// <param name="value">When this method returns, contains the value associated with the specified arrayIndex, if th
 482    /// <returns><c>true</c> if the bucket contains an element at the specified arrayIndex; otherwise, <c>false</c>.</re
 483    public bool TryGetValue(int key, out T value)
 484    {
 4485        if (!IsAllocated(key))
 486        {
 3487            value = default!;
 3488            return false;
 489        }
 490
 1491        value = _innerArray[key].Value;
 1492        return true;
 493    }
 494
 495    /// <summary>
 496    /// Determines whether the bucket contains a specific value.
 497    /// </summary>
 498    /// <param name="item">The object to locate in the bucket.</param>
 499    /// <returns><c>true</c> if item is found; otherwise, <c>false</c>.</returns>
 500    /// <remarks>
 501    /// This method performs a linear search and has a time complexity of O(n).
 502    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 503    /// </remarks>
 504    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 6505    public bool Contains(T item) => IndexOf(item) != -1;
 506
 507    /// <summary>
 508    /// Determines whether the <see cref="SwiftBucket{T}"/> contains an element that matches the conditions defined by t
 509    /// </summary>
 510    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 511    /// <returns><c>true</c> if the <see cref="SwiftBucket{T}"/> contains one or more elements that match the specified 
 512    public bool Exists(Predicate<T> match)
 513    {
 2514        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 515
 2516        uint count = 0;
 517
 12518        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 519        {
 5520            if (_innerArray[i].IsUsed)
 521            {
 5522                if (match(_innerArray[i].Value))
 1523                    return true;
 524
 4525                count++;
 526            }
 527        }
 528
 1529        return false;
 530    }
 531
 532    /// <summary>
 533    /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first ma
 534    /// </summary>
 535    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 536    /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, 
 537    public T Find(Predicate<T> match)
 538    {
 2539        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 540
 2541        uint count = 0;
 542
 10543        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 544        {
 4545            if (_innerArray[i].IsUsed)
 546            {
 4547                T item = _innerArray[i].Value;
 4548                if (match(item))
 1549                    return item;
 550
 3551                count++;
 552            }
 553        }
 554
 1555        return default!;
 556    }
 557
 558    /// <summary>
 559    /// Determines whether the element at the specified index is currently allocated.
 560    /// </summary>
 561    /// <param name="index">The zero-based index of the element to check. Must be greater than or equal to 0 and less th
 562    /// <returns>true if the element at the specified index is allocated; otherwise, false.</returns>
 563    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50495564    public bool IsAllocated(int index) => !((uint)index >= (uint)_innerArray.Length) && _innerArray[index].IsUsed;
 565
 566    /// <summary>
 567    /// Searches for the specified object and returns the zero-based arrayIndex of the first occurrence within the bucke
 568    /// </summary>
 569    /// <param name="item">The object to locate in the bucket.</param>
 570    /// <returns>
 571    /// The zero-based arrayIndex of the first occurrence of <paramref name="item"/> within the bucket, if found; otherw
 572    /// </returns>
 573    /// <remarks>
 574    /// This method performs a linear search and has a time complexity of O(n).
 575    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 576    /// </remarks>
 577    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 578    public int IndexOf(T item)
 579    {
 11580        uint count = 0;
 581
 11582        if (item == null)
 583        {
 14584            for (int i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 585            {
 6586                if (_innerArray[i].IsUsed)
 587                {
 6588                    if (_innerArray[i].Value == null)
 2589                        return i;
 4590                    count++;
 591                }
 592            }
 593
 1594            return -1;
 595        }
 596
 40597        for (int j = 0; j < (uint)_peakCount && count < (uint)_count; j++)
 598        {
 17599            if (_innerArray[j].IsUsed)
 600            {
 12601                if (EqualityComparer<T>.Default.Equals(_innerArray[j].Value, item))
 5602                    return j;
 7603                count++;
 604            }
 605        }
 3606        return -1;
 607    }
 608
 609    /// <summary>
 610    /// Copies the elements of the bucket to an <see cref="Array"/>, starting at a particular Array arrayIndex.
 611    /// </summary>
 612    /// <param name="array">The one-dimensional Array that is the destination of the elements copied from bucket.</param
 613    /// <param name="arrayIndex">The zero-based arrayIndex in array at which copying begins.</param>
 614    public void CopyTo(T[] array, int arrayIndex)
 615    {
 1616        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 1617        SwiftThrowHelper.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 1618        SwiftThrowHelper.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "The array is not large enou
 619
 1620        uint count = 0;
 8621        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 622        {
 3623            if (_innerArray[i].IsUsed)
 624            {
 3625                array[arrayIndex++] = _innerArray[i].Value;
 3626                count++;
 627            }
 628        }
 1629    }
 630
 631    void ICollection.CopyTo(Array array, int arrayIndex)
 632    {
 7633        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 7634        SwiftThrowHelper.ThrowIfArgument((uint)array.Rank != 1, nameof(array), "Array must be single dimensional.");
 6635        SwiftThrowHelper.ThrowIfArgument((uint)array.GetLowerBound(0) != 0, nameof(array), "Array must have zero-based i
 5636        SwiftThrowHelper.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 4637        SwiftThrowHelper.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "The array is not large enou
 638
 639        try
 640        {
 3641            uint count = 0;
 10642            for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 643            {
 4644                if (_innerArray[i].IsUsed)
 645                {
 4646                    array.SetValue(_innerArray[i].Value, arrayIndex++);
 2647                    count++;
 648                }
 649            }
 1650        }
 2651        catch (InvalidCastException)
 652        {
 2653            throw new ArgumentException("Invalid array type.");
 654        }
 1655    }
 656
 657    /// <inheritdoc/>
 658    public void CloneTo(ICollection<T> output)
 659    {
 1660        output.Clear();
 1661        uint count = 0;
 6662        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 663        {
 2664            if (_innerArray[i].IsUsed)
 665            {
 2666                output.Add(_innerArray[i].Value);
 2667                count++;
 668            }
 669        }
 1670    }
 671
 672    #endregion
 673
 674    #region Enumerator
 675
 676    /// <summary>
 677    /// Returns an enumerator that iterates through the <see cref="SwiftBucket{T}"/>.
 678    /// </summary>
 679    /// <returns>An enumerator for the bucket.</returns>
 8680    public SwiftBucketEnumerator GetEnumerator() => new(this);
 4681    IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
 1682    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 683
 684    /// <summary>
 685    /// Enumerates the elements of a <see cref="SwiftBucket{T}"/> collection.
 686    /// </summary>
 687    /// <remarks>
 688    /// The enumerator provides read-only, forward-only iteration over the elements in the <see cref="SwiftBucket{T}"/>.
 689    /// The enumerator is invalidated if the collection is modified after the enumerator is created.
 690    /// </remarks>
 691    public struct SwiftBucketEnumerator : IEnumerator<T>, IDisposable
 692    {
 693        private readonly SwiftBucket<T> _bucket;
 694        private readonly Entry[] _entries;
 695        private readonly uint _version;
 696        private int _index;
 697        private T _current;
 698
 699        internal SwiftBucketEnumerator(SwiftBucket<T> bucket)
 700        {
 8701            _bucket = bucket;
 8702            _entries = bucket._innerArray;
 8703            _version = bucket._version;
 8704            _index = -1;
 8705            _current = default!;
 8706        }
 707
 708        /// <inheritdoc/>
 410709        public T Current => _current;
 710
 711        object IEnumerator.Current
 712        {
 713            get
 714            {
 1715                SwiftThrowHelper.ThrowIfTrue(_index > (uint)_bucket._count, message: "Enumerator is before the first ele
 1716                return _current!;
 717            }
 718        }
 719
 720        /// <inheritdoc/>
 721        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 722        public bool MoveNext()
 723        {
 219724            SwiftThrowHelper.ThrowIfTrue(_version != _bucket._version, message: "Enumerator modified outside of enumerat
 725
 218726            uint count = (uint)_bucket._peakCount;
 219727            while (++_index < count)
 728            {
 212729                if (_entries[_index].IsUsed)
 730                {
 211731                    _current = _entries[_index].Value;
 211732                    return true;
 733                }
 734            }
 7735            return false;
 736        }
 737
 738        /// <inheritdoc/>
 739        public void Reset()
 740        {
 1741            SwiftThrowHelper.ThrowIfTrue(_version != _bucket._version, message: "Enumerator modified outside of enumerat
 742
 1743            _index = -1;
 1744            _current = default!;
 1745        }
 746
 747        /// <inheritdoc/>
 6748        public void Dispose() => _index = -1;
 749    }
 750
 751    #endregion
 752}