< Summary

Line coverage
100%
Covered lines: 244
Uncovered lines: 0
Coverable lines: 244
Total lines: 789
Line coverage: 100%
Branch coverage
100%
Covered branches: 132
Total branches: 132
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor()100%11100%
File 1: .ctor(...)100%22100%
File 1: .ctor(...)100%11100%
File 1: get_Count()100%11100%
File 1: get_PeakCount()100%11100%
File 1: get_Capacity()100%11100%
File 1: get_Item(...)100%11100%
File 1: set_Item(...)100%11100%
File 1: get_IsReadOnly()100%11100%
File 1: get_IsSynchronized()100%11100%
File 1: get_SyncRoot()100%22100%
File 1: get_State()100%44100%
File 1: set_State(...)100%88100%
File 1: RestoreAllocatedEntries(...)100%88100%
File 1: RestoreFreeIndices(...)100%44100%
File 1: NormalizePeakCount(...)100%44100%
File 1: Add(...)100%44100%
File 1: System.Collections.Generic.ICollection<T>.Add(...)100%11100%
File 1: InsertAt(...)100%66100%
File 1: TryRemove(...)100%22100%
File 1: System.Collections.Generic.ICollection<T>.Remove(...)100%11100%
File 1: TryRemoveAt(...)100%22100%
File 1: RemoveAt(...)100%11100%
File 1: Clear()100%44100%
File 1: Clear(...)100%66100%
File 1: EnsureCapacity(...)100%22100%
File 1: Resize(...)100%22100%
File 1: TrimExcessCapacity()100%44100%
File 1: GetLivePeakCount()100%44100%
File 1: RebuildFreeIndices()100%44100%
File 1: TryGetValue(...)100%22100%
File 1: Contains(...)100%11100%
File 1: Exists(...)100%88100%
File 1: Find(...)100%88100%
File 1: IsAllocated(...)100%22100%
File 1: IndexOf(...)100%1818100%
File 1: CopyTo(...)100%66100%
File 1: System.Collections.ICollection.CopyTo(...)100%66100%
File 1: CloneTo(...)100%66100%
File 1: GetEnumerator()100%11100%
File 1: System.Collections.Generic.IEnumerable<T>.GetEnumerator()100%11100%
File 1: 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%11100%
File 2: MoveNext()100%44100%
File 2: Reset()100%11100%
File 2: Dispose()100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// SwiftSlotMap.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 System;
 9using System.Collections;
 10using System.Collections.Generic;
 11using System.Runtime.CompilerServices;
 12using System.Text.Json.Serialization;
 13using Chronicler;
 14using Chronicler.Diagnostics;
 15using Chronicler.Serialization;
 16using MemoryPack;
 17using SwiftCollections.Utility;
 18
 19namespace SwiftCollections;
 20
 21/// <summary>
 22/// Represents a high-performance map collection that assigns and manages stable integer indices
 23/// for stored items. Provides O(1) insertion, removal, and lookup by internally generated index.
 24/// </summary>
 25/// <remarks>
 26/// Unlike <see cref="SwiftSparseMap{T}"/>, which requires callers to provide the key used to store values,
 27/// <see cref="SwiftSlotMap{T}"/> internally generates and manages indices for each inserted item.
 28///
 29/// These indices remain stable for the lifetime of the item unless it is removed.
 30///
 31/// The container is optimized for scenarios requiring:
 32/// <list type="bullet">
 33///     <item>
 34///         <description>Stable integer slots.</description>
 35///     </item>
 36///     <item>
 37///         <description>Fast addition and removal.</description>
 38///     </item>
 39///     <item>
 40///         <description>Array-backed storage; iteration scans allocated slots and skips holes.</description>
 41///     </item>
 42/// </list>
 43///
 44/// **Efficient Lookups Using Indices**:
 45/// When you add items to the map using the <see cref="Add"/> method, it returns an arrayIndex that you can store extern
 46/// You can then use this arrayIndex to access the item directly via the indexer, and check if it's still present using 
 47/// This approach allows for O(1) time complexity for lookups and existence checks, avoiding the need for O(n) searches 
 48///
 49/// **Note**: iteration over the collection does not follow any guaranteed order and depends on internal allocation.
 50/// </remarks>
 51/// <typeparam name="T">Specifies the type of elements in the map.</typeparam>
 52[Serializable]
 53[JsonConverter(typeof(StateJsonConverterFactory))]
 54[MemoryPackable]
 55public sealed partial class SwiftSlotMap<T> : IStateBacked<SwiftSlotMapState<T>>, ISwiftCloneable<T>, IEnumerable<T>, IC
 56{
 57    #region Constants
 58
 59    /// <summary>
 60    /// Represents the default initial capacity used when no specific capacity is provided.
 61    /// </summary>
 62    public const int DefaultCapacity = 8;
 63
 64    #endregion
 65
 66    #region Fields
 67
 68    private Entry[] _innerArray;
 69
 70    private int _count;
 71
 72    private int _peakCount;
 73
 74    private SwiftIntStack _freeIndices;
 75
 76    [NonSerialized]
 77    private uint _version;
 78
 79    [NonSerialized]
 80    private object? _syncRoot;
 81
 82    #endregion
 83
 84    #region Nested Types
 85
 86    [Serializable]
 87    private struct Entry
 88    {
 89        public T Value;
 90        public bool IsUsed;
 91    }
 92
 93    #endregion
 94
 95    #region Constructors
 96
 97    /// <summary>
 98    /// Initializes a new instance of the <see cref="SwiftSlotMap{T}"/> class.
 99    /// </summary>
 116100    public SwiftSlotMap() : this(DefaultCapacity) { }
 101
 102    /// <summary>
 103    /// Initializes a new instance of the <see cref="SwiftSlotMap{T}"/> class with the specified capacity.
 104    /// </summary>
 105    /// <param name="capacity">The initial capacity of the map.</param>
 69106    public SwiftSlotMap(int capacity)
 107    {
 69108        capacity = capacity <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(capacity);
 69109        _innerArray = new Entry[capacity];
 69110        _freeIndices = new SwiftIntStack(capacity);
 69111    }
 112
 113    ///  <summary>
 114    ///  Initializes a new instance of the <see cref="SwiftSlotMap{T}"/> class with the specified <see cref="SwiftArrayS
 115    ///  </summary>
 116    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 117    [MemoryPackConstructor]
 11118    public SwiftSlotMap(SwiftSlotMapState<T> state)
 119    {
 11120        State = state;
 121
 10122        ChronicleGuard.ThrowIfNull(_innerArray, nameof(state.Items));
 10123        ChronicleGuard.ThrowIfNull(_freeIndices, nameof(state.FreeIndices));
 10124    }
 125
 126    #endregion
 127
 128    #region Properties
 129
 130    /// <summary>
 131    /// Gets the number of elements contained in the <see cref="SwiftSlotMap{T}"/>.
 132    /// </summary>
 133    [JsonIgnore]
 134    [MemoryPackIgnore]
 124135    public int Count => _count;
 136
 137    /// <summary>
 138    /// Gets the highest value recorded for the count during the lifetime of the object.
 139    /// </summary>
 140    [JsonIgnore]
 141    [MemoryPackIgnore]
 10142    public int PeakCount => _peakCount;
 143
 144    /// <summary>
 145    /// Gets the total capacity of the <see cref="SwiftSlotMap{T}"/>.
 146    /// </summary>
 147    [JsonIgnore]
 148    [MemoryPackIgnore]
 18149    public int Capacity => _innerArray.Length;
 150
 151    /// <summary>
 152    /// Gets or sets the element at the specified arrayIndex.
 153    /// Throws <see cref="InvalidOperationException"/> if the arrayIndex is invalid or unallocated.
 154    /// </summary>
 155    /// <param name="index">The zero-based arrayIndex of the element to get or set.</param>
 156    [JsonIgnore]
 157    [MemoryPackIgnore]
 158    public T this[int index]
 159    {
 160        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 161        get
 162        {
 237163            ChronicleGuard.ThrowIfTrue(!IsAllocated(index), nameof(index), message: "Index is out of range or unallocate
 234164            return _innerArray[index].Value;
 165        }
 166        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 167        set
 168        {
 1169            ChronicleGuard.ThrowIfTrue(!IsAllocated(index), nameof(index), message: "Index is out of range or unallocate
 1170            _innerArray[index].Value = value;
 1171            _version++;
 1172        }
 173    }
 174
 175    ///<inheritdoc/>
 176    [JsonIgnore]
 177    [MemoryPackIgnore]
 1178    public bool IsReadOnly => false;
 179
 180    ///<inheritdoc/>
 181    [JsonIgnore]
 182    [MemoryPackIgnore]
 1183    public bool IsSynchronized => false;
 184
 185    ///<inheritdoc/>
 186    [JsonIgnore]
 187    [MemoryPackIgnore]
 3188    public object SyncRoot => _syncRoot ??= new object();
 189
 190    /// <summary>
 191    /// Gets or sets the current state of the map, including all items, allocation status, and free indices.
 192    /// </summary>
 193    /// <remarks>
 194    /// Use this property to capture or restore the complete state of the map, such as for serialization or checkpointin
 195    /// Setting this property replaces the entire internal state, including items and allocation metadata.
 196    /// </remarks>
 197    [JsonInclude]
 198    [MemoryPackInclude]
 199    public SwiftSlotMapState<T> State
 200    {
 201        get
 202        {
 4203            int length = _innerArray.Length;
 204
 4205            var items = new T[length];
 4206            var allocated = new bool[length];
 207
 552208            for (int i = 0; i < length; i++)
 209            {
 272210                if (_innerArray[i].IsUsed)
 211                {
 204212                    items[i] = _innerArray[i].Value;
 204213                    allocated[i] = true;
 214                }
 215            }
 216
 4217            int[] free = new int[_freeIndices.Count];
 4218            Array.Copy(_freeIndices.Array, free, _freeIndices.Count);
 219
 4220            return new SwiftSlotMapState<T>(
 4221                items,
 4222                allocated,
 4223                free,
 4224                _peakCount
 4225            );
 226        }
 227        internal set
 228        {
 11229            var items = value.Items ?? Array.Empty<T>();
 11230            var allocated = value.Allocated ?? Array.Empty<bool>();
 11231            var freeIndices = value.FreeIndices ?? Array.Empty<int>();
 232
 11233            int sourceLength = Math.Max(items.Length, allocated.Length);
 11234            int capacity = sourceLength < DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(sourceLength
 235
 11236            _innerArray = new Entry[capacity];
 11237            _freeIndices = new SwiftIntStack(capacity);
 238
 11239            _count = 0;
 11240            int maxReferencedIndex = RestoreAllocatedEntries(items, allocated, sourceLength);
 11241            maxReferencedIndex = RestoreFreeIndices(freeIndices, capacity, maxReferencedIndex);
 10242            _peakCount = NormalizePeakCount(value.PeakCount, maxReferencedIndex, capacity);
 10243            _version = 0;
 10244        }
 245    }
 246
 247    private int RestoreAllocatedEntries(T[] items, bool[] allocated, int sourceLength)
 248    {
 11249        int maxReferencedIndex = -1;
 584250        for (int i = 0; i < sourceLength; i++)
 251        {
 281252            if (allocated.Length <= i || !allocated[i])
 253                continue;
 254
 211255            if (items.Length > i)
 210256                _innerArray[i].Value = items[i];
 257
 211258            _innerArray[i].IsUsed = true;
 211259            _count++;
 211260            maxReferencedIndex = i;
 261        }
 262
 11263        return maxReferencedIndex;
 264    }
 265
 266    private int RestoreFreeIndices(int[] freeIndices, int capacity, int maxReferencedIndex)
 267    {
 33268        foreach (var index in freeIndices)
 269        {
 6270            ChronicleGuard.ThrowIfTrue((uint)index >= (uint)capacity, message: "Free index is out of range.");
 271
 5272            _freeIndices.Push(index);
 5273            if (index > maxReferencedIndex)
 2274                maxReferencedIndex = index;
 275        }
 276
 10277        return maxReferencedIndex;
 278    }
 279
 280    private static int NormalizePeakCount(int peakCount, int maxReferencedIndex, int capacity)
 281    {
 10282        if (peakCount < 0)
 1283            peakCount = 0;
 284
 10285        int normalizedPeak = Math.Max(peakCount, maxReferencedIndex + 1);
 10286        return normalizedPeak > capacity ? capacity : normalizedPeak;
 287    }
 288
 289    #endregion
 290
 291    #region Collection Management
 292
 293    /// <summary>
 294    /// Adds an item to the map and returns its arrayIndex.
 295    /// </summary>
 296    /// <param name="item">The item to add.</param>
 297    /// <returns>The arrayIndex where the item was added.</returns>
 298    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 299    public int Add(T item)
 300    {
 301        int index;
 100717302        if ((uint)_freeIndices.Count == 0)
 303        {
 100584304            index = _peakCount++;
 100584305            if ((uint)index >= (uint)_innerArray.Length)
 24306                Resize(_innerArray.Length * 2);
 307        }
 133308        else index = _freeIndices.Pop();
 309
 100717310        _innerArray[index].Value = item;
 100717311        _innerArray[index].IsUsed = true;
 100717312        _count++;
 100717313        _version++;
 100717314        return index;
 315    }
 316
 1317    void ICollection<T>.Add(T item) => Add(item);
 318
 319    /// <summary>
 320    /// Inserts an item at the specified arrayIndex.
 321    /// If an item already exists at that arrayIndex, it will be replaced.
 322    /// </summary>
 323    /// <param name="index">The arrayIndex at which to insert the item.</param>
 324    /// <param name="item">The item to insert.</param>
 325    public void InsertAt(int index, T item)
 326    {
 8327        ChronicleGuard.ThrowIfNegative(index, nameof(index));
 8328        if ((uint)index >= (uint)_innerArray.Length)
 1329            Resize(SwiftHashTools.NextPowerOfTwo(index + 1));
 8330        if (!_innerArray[index].IsUsed)
 331        {
 7332            _count++;
 7333            if ((uint)index >= (uint)_peakCount)
 6334                _peakCount = index + 1;
 335        }
 8336        _innerArray[index].Value = item;
 8337        _innerArray[index].IsUsed = true;
 8338        _version++;
 8339    }
 340
 341    /// <summary>
 342    /// Removes the first occurrence of a specific object from the map.
 343    /// </summary>
 344    /// <param name="item">The object to remove.</param>
 345    /// <returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
 346    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 347    public bool TryRemove(T item)
 348    {
 3349        int index = IndexOf(item);
 4350        if (index < 0) return false;
 2351        RemoveAt(index);
 2352        return true;
 353    }
 354
 1355    bool ICollection<T>.Remove(T item) => TryRemove(item);
 356
 357    /// <summary>
 358    /// Removes the item at the specified arrayIndex if it has been allocated.
 359    /// </summary>
 360    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 361    public bool TryRemoveAt(int index)
 362    {
 50260363        if (IsAllocated(index))
 364        {
 50259365            RemoveAt(index);
 50259366            return true;
 367        }
 1368        return false;
 369    }
 370
 371    /// <summary>
 372    /// Removes the item at the specified arrayIndex.
 373    /// </summary>
 374    /// <param name="index">The arrayIndex of the item to remove.</param>
 375    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 376    public void RemoveAt(int index)
 377    {
 50272378        _innerArray[index] = default;
 50272379        _count--;
 50272380        _freeIndices.Push(index);
 50272381        _version++;
 50272382    }
 383
 384    /// <summary>
 385    /// Removes all items from the map.
 386    /// </summary>
 387    public void Clear()
 388    {
 147389        if ((uint)_count == 0) return;
 536390        for (int i = 0; i < _peakCount; i++)
 135391            _innerArray[i] = default;
 133392        _freeIndices.Reset();
 133393        _count = 0;
 133394        _peakCount = 0;
 133395        _version++;
 133396    }
 397
 398    /// <summary>
 399    /// Removes all elements, optionally releasing references throughout retained backing storage.
 400    /// </summary>
 401    /// <param name="clearRetainedReferences">If true, also releases references outside the live range; otherwise uses o
 402    /// <remarks>Capacity is preserved. Full reference cleanup can take time proportional to retained capacity.</remarks
 403    public void Clear(bool clearRetainedReferences)
 404    {
 138405        if (clearRetainedReferences && RuntimeHelpers.IsReferenceOrContainsReferences<T>())
 406        {
 131407            int start = _count == 0 ? 0 : _peakCount;
 131408            Array.Clear(_innerArray, start, _innerArray.Length - start);
 409        }
 138410        Clear();
 138411    }
 412
 413    #endregion
 414
 415    #region Capacity Management
 416
 417    /// <summary>
 418    /// Ensures that the internal storage has at least the specified capacity, expanding it if necessary.
 419    /// </summary>
 420    /// <remarks>
 421    /// If the current capacity is less than the specified value, the internal storage is increased to
 422    /// the next power of two greater than or equal to the requested capacity.
 423    /// No action is taken if the current capacity is sufficient.
 424    /// </remarks>
 425    /// <param name="capacity">The minimum number of elements that the internal storage should be able to hold. Must be 
 426    public void EnsureCapacity(int capacity)
 427    {
 4428        capacity = SwiftHashTools.NextPowerOfTwo(capacity);
 4429        if (capacity > _innerArray.Length)
 3430            Resize(capacity);
 431        else
 1432            _freeIndices.EnsureCapacity(capacity);
 1433    }
 434
 435    private void Resize(int newSize)
 436    {
 28437        int newCapacity = newSize;
 28438        int copyLength = Math.Min(_peakCount, _innerArray.Length);
 439
 28440        Entry[] newArray = new Entry[newCapacity];
 28441        if (copyLength > 0)
 27442            Array.Copy(_innerArray, 0, newArray, 0, copyLength);
 28443        _innerArray = newArray;
 28444        _freeIndices.EnsureCapacity(newCapacity);
 445
 28446        _version++;
 28447    }
 448
 449    /// <summary>
 450    /// Reduces unused tail capacity while preserving stable indices for all currently allocated entries.
 451    /// </summary>
 452    public void TrimExcessCapacity()
 453    {
 4454        int newPeak = GetLivePeakCount();
 4455        int newCapacity = newPeak <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(newPeak);
 456
 4457        Entry[] newArray = new Entry[newCapacity];
 4458        if (newPeak > 0)
 2459            Array.Copy(_innerArray, 0, newArray, 0, newPeak);
 460
 4461        _innerArray = newArray;
 4462        _peakCount = newPeak;
 4463        RebuildFreeIndices();
 464
 4465        _version++;
 4466    }
 467
 468    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 469    private int GetLivePeakCount()
 470    {
 10471        for (int i = _peakCount - 1; i >= 0; i--)
 3472            if (_innerArray[i].IsUsed)
 2473                return i + 1;
 474
 2475        return 0;
 476    }
 477
 478    private void RebuildFreeIndices()
 479    {
 4480        _freeIndices = new SwiftIntStack(_innerArray.Length);
 481
 78482        for (int i = 0; i < _peakCount; i++)
 483        {
 35484            if (!_innerArray[i].IsUsed)
 30485                _freeIndices.Push(i);
 486        }
 4487    }
 488
 489    #endregion
 490
 491    #region Utility Methods
 492
 493    /// <summary>
 494    /// Attempts to get the value at the specified arrayIndex.
 495    /// </summary>
 496    /// <param name="key">The arrayIndex of the item to get.</param>
 497    /// <param name="value">When this method returns, contains the value associated with the specified arrayIndex, if th
 498    /// <returns><c>true</c> if the map contains an element at the specified arrayIndex; otherwise, <c>false</c>.</retur
 499    public bool TryGetValue(int key, out T value)
 500    {
 4501        if (!IsAllocated(key))
 502        {
 3503            value = default!;
 3504            return false;
 505        }
 506
 1507        value = _innerArray[key].Value;
 1508        return true;
 509    }
 510
 511    /// <summary>
 512    /// Determines whether the map contains a specific value.
 513    /// </summary>
 514    /// <param name="item">The object to locate in the map.</param>
 515    /// <returns><c>true</c> if item is found; otherwise, <c>false</c>.</returns>
 516    /// <remarks>
 517    /// This method performs a linear search and has a time complexity of O(n).
 518    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 519    /// </remarks>
 520    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 6521    public bool Contains(T item) => IndexOf(item) != -1;
 522
 523    /// <summary>
 524    /// Determines whether the <see cref="SwiftSlotMap{T}"/> contains an element that matches the conditions defined by 
 525    /// </summary>
 526    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 527    /// <returns><c>true</c> if the <see cref="SwiftSlotMap{T}"/> contains one or more elements that match the specified
 528    public bool Exists(Predicate<T> match)
 529    {
 3530        ChronicleGuard.ThrowIfNull(match, nameof(match));
 531
 3532        uint count = 0;
 533
 18534        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 535        {
 7536            if (_innerArray[i].IsUsed)
 537            {
 6538                if (match(_innerArray[i].Value))
 1539                    return true;
 540
 5541                count++;
 542            }
 543        }
 544
 2545        return false;
 546    }
 547
 548    /// <summary>
 549    /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first ma
 550    /// </summary>
 551    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 552    /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, 
 553    public T Find(Predicate<T> match)
 554    {
 3555        ChronicleGuard.ThrowIfNull(match, nameof(match));
 556
 3557        uint count = 0;
 558
 14559        for (int i = 0; i < _peakCount && count < (uint)_count; i++)
 560        {
 6561            if (_innerArray[i].IsUsed)
 562            {
 5563                T item = _innerArray[i].Value;
 5564                if (match(item))
 2565                    return item;
 566
 3567                count++;
 568            }
 569        }
 570
 1571        return default!;
 572    }
 573
 574    /// <summary>
 575    /// Determines whether the element at the specified index is currently allocated.
 576    /// </summary>
 577    /// <param name="index">The zero-based index of the element to check. Must be greater than or equal to 0 and less th
 578    /// <returns>true if the element at the specified index is allocated; otherwise, false.</returns>
 579    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50508580    public bool IsAllocated(int index) => !((uint)index >= (uint)_innerArray.Length) && _innerArray[index].IsUsed;
 581
 582    /// <summary>
 583    /// Searches for the specified object and returns the zero-based arrayIndex of the first occurrence within the map.
 584    /// </summary>
 585    /// <param name="item">The object to locate in the map.</param>
 586    /// <returns>
 587    /// The zero-based arrayIndex of the first occurrence of <paramref name="item"/> within the map, if found; otherwise
 588    /// </returns>
 589    /// <remarks>
 590    /// This method performs a linear search and has a time complexity of O(n).
 591    /// It is recommended to store the indices returned by the <see cref="Add"/> method for faster lookups using the ind
 592    /// </remarks>
 593    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 594    public int IndexOf(T item)
 595    {
 12596        uint count = 0;
 597
 12598        if (item == null)
 599        {
 20600            for (int i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 601            {
 8602                if (_innerArray[i].IsUsed)
 603                {
 7604                    if (_innerArray[i].Value == null)
 2605                        return i;
 5606                    count++;
 607                }
 608            }
 609
 2610            return -1;
 611        }
 612
 40613        for (int j = 0; j < (uint)_peakCount && count < (uint)_count; j++)
 614        {
 17615            if (_innerArray[j].IsUsed)
 616            {
 12617                if (EqualityComparer<T>.Default.Equals(_innerArray[j].Value, item))
 5618                    return j;
 7619                count++;
 620            }
 621        }
 3622        return -1;
 623    }
 624
 625    /// <summary>
 626    /// Copies the elements of the map to an <see cref="Array"/>, starting at a particular Array arrayIndex.
 627    /// </summary>
 628    /// <param name="array">The one-dimensional Array that is the destination of the elements copied from map.</param>
 629    /// <param name="arrayIndex">The zero-based arrayIndex in array at which copying begins.</param>
 630    public void CopyTo(T[] array, int arrayIndex)
 631    {
 2632        ChronicleGuard.ThrowIfNull(array, nameof(array));
 2633        ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 2634        ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "The array is not large enough
 635
 2636        uint count = 0;
 14637        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 638        {
 5639            if (_innerArray[i].IsUsed)
 640            {
 4641                array[arrayIndex++] = _innerArray[i].Value;
 4642                count++;
 643            }
 644        }
 2645    }
 646
 647    void ICollection.CopyTo(Array array, int arrayIndex)
 648    {
 7649        ChronicleGuard.ThrowIfNull(array, nameof(array));
 7650        ChronicleGuard.ThrowIfArgument((uint)array.Rank != 1, nameof(array), "Array must be single dimensional.");
 6651        ChronicleGuard.ThrowIfArgument((uint)array.GetLowerBound(0) != 0, nameof(array), "Array must have zero-based ind
 5652        ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 4653        ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "The array is not large enough
 654
 655        try
 656        {
 3657            uint count = 0;
 10658            for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 659            {
 4660                if (_innerArray[i].IsUsed)
 661                {
 4662                    array.SetValue(_innerArray[i].Value, arrayIndex++);
 2663                    count++;
 664                }
 665            }
 1666        }
 2667        catch (InvalidCastException)
 668        {
 2669            throw new ArgumentException("Invalid array type.");
 670        }
 1671    }
 672
 673    /// <inheritdoc/>
 674    public void CloneTo(ICollection<T> output)
 675    {
 2676        output.Clear();
 2677        uint count = 0;
 12678        for (uint i = 0; i < (uint)_peakCount && count < (uint)_count; i++)
 679        {
 4680            if (_innerArray[i].IsUsed)
 681            {
 3682                output.Add(_innerArray[i].Value);
 3683                count++;
 684            }
 685        }
 2686    }
 687
 688    #endregion
 689
 690    #region Enumerator
 691
 692    /// <summary>
 693    /// Returns an enumerator that iterates through the <see cref="SwiftSlotMap{T}"/>.
 694    /// </summary>
 695    /// <returns>An enumerator for the map.</returns>
 51696    public SwiftSlotMapEnumerator GetEnumerator() => new(this);
 28697    IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
 20698    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 699
 700
 701
 702    #endregion
 703}

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftSlotMap.SwiftSlotMapEnumerator.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftSlotMap.SwiftSlotMapEnumerator.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 System;
 9using System.Collections;
 10using System.Collections.Generic;
 11using System.Runtime.CompilerServices;
 12
 13using Chronicler.Diagnostics;
 14
 15
 16namespace SwiftCollections;
 17
 18public sealed partial class SwiftSlotMap<T>
 19{
 20    /// <summary>
 21    /// Enumerates the elements of a <see cref="SwiftSlotMap{T}"/> collection.
 22    /// </summary>
 23    /// <remarks>
 24    /// The enumerator provides read-only, forward-only iteration over the elements in the <see cref="SwiftSlotMap{T}"/>
 25    /// The enumerator is invalidated if the collection is modified after the enumerator is created.
 26    /// </remarks>
 27    public struct SwiftSlotMapEnumerator : IEnumerator<T>, IDisposable
 28    {
 29        private readonly SwiftSlotMap<T> _bucket;
 30        private readonly Entry[] _entries;
 31        private readonly uint _version;
 32        private int _index;
 33        private T _current;
 34
 35        internal SwiftSlotMapEnumerator(SwiftSlotMap<T> map)
 36        {
 5137            _bucket = map;
 5138            _entries = map._innerArray;
 5139            _version = map._version;
 5140            _index = -1;
 5141            _current = default!;
 5142        }
 43
 44        /// <inheritdoc/>
 43445        public T Current => _current;
 46
 47        object IEnumerator.Current
 48        {
 49            get
 50            {
 3351                ChronicleGuard.ThrowIfTrue((uint)_index >= (uint)_bucket._peakCount, message: "Enumerator is before the 
 1452                return _current!;
 53            }
 54        }
 55
 56        /// <inheritdoc/>
 57        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 58        public bool MoveNext()
 59        {
 30860            ChronicleGuard.ThrowIfTrue(_version != _bucket._version, message: "Enumerator modified outside of enumeratio
 61
 30462            uint count = (uint)_bucket._peakCount;
 31463            while (++_index < count)
 64            {
 26665                if (_entries[_index].IsUsed)
 66                {
 25667                    _current = _entries[_index].Value;
 25668                    return true;
 69                }
 70            }
 4871            return false;
 72        }
 73
 74        /// <inheritdoc/>
 75        public void Reset()
 76        {
 1077            ChronicleGuard.ThrowIfTrue(_version != _bucket._version, message: "Enumerator modified outside of enumeratio
 78
 779            _index = -1;
 780            _current = default!;
 781        }
 82
 83        /// <inheritdoc/>
 3384        public void Dispose() => _index = -1;
 85    }
 86}