< Summary

Information
Class: SwiftCollections.SwiftSparseMap<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftSparseMap.cs
Line coverage
99%
Covered lines: 211
Uncovered lines: 2
Coverable lines: 213
Total lines: 703
Line coverage: 99%
Branch coverage
87%
Covered branches: 77
Total branches: 88
Branch coverage: 87.5%
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%1010100%
.ctor(...)50%66100%
get_Count()100%11100%
get_DenseCapacity()100%11100%
get_SparseCapacity()100%11100%
get_IsSynchronized()100%11100%
get_SyncRoot()100%22100%
get_DenseKeys()100%11100%
get_Keys()100%11100%
get_DenseValues()100%11100%
get_Values()100%11100%
get_Item(...)100%11100%
set_Item(...)100%22100%
get_State()100%11100%
set_State(...)78.57%1414100%
ContainsKey(...)100%22100%
TryAdd(...)100%22100%
Add(...)100%11100%
TryGetValue(...)100%44100%
Remove(...)83.33%66100%
Clear()100%66100%
EnsureDenseCapacity(...)75%8884.61%
EnsureSparseCapacity(...)87.5%88100%
TrimExcess()92.85%1414100%
GetEnumerator()100%11100%
System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Int32,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%22100%
Reset()100%11100%
Dispose()100%11100%
GetDense(...)100%11100%
GetRequiredSparseCapacity(...)100%11100%
GetDenseIndexOrThrow(...)100%11100%
CloneTo(...)100%22100%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftSparseMap.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 sparse map that stores values indexed by externally supplied integer keys.
 13/// Provides O(1) Add, Remove, Contains, and lookup operations while maintaining densely packed storage
 14/// for cache-friendly iteration.
 15/// </summary>
 16/// <remarks>
 17/// Unlike <see cref="SwiftBucket{T}"/>, which internally assigns and manages item indices,
 18/// <see cref="SwiftSparseMap{T}"/> is externally keyed. The caller supplies the integer key
 19/// (for example, an entity ID or handle) used to index the value.
 20///
 21/// Internally, the container maintains:
 22/// <list type="bullet">
 23///     <item>
 24///         <description>A sparse lookup table mapping keys to dense indices.</description>
 25///     </item>
 26///     <item>
 27///         <description>A dense array of keys.</description>
 28///     </item>
 29///     <item>
 30///         <description>A dense array of values.</description>
 31///     </item>
 32/// </list>
 33///
 34/// Removal uses a swap-back strategy to keep dense storage contiguous. As a result,
 35/// iteration order is not guaranteed to remain stable.
 36///
 37/// Keys are used as direct indices into the sparse lookup table, so memory usage scales
 38/// with the highest stored key rather than the number of stored values. This container is
 39/// intended for compact, non-negative IDs such as entity handles or slot indices. It is
 40/// not a good fit for arbitrary hashes or widely spaced keys; for those workloads prefer
 41/// <c>SwiftDictionary&lt;TKey, TValue&gt;</c>.
 42/// </remarks>
 43/// <typeparam name="T">Value type stored by key.</typeparam>
 44[Serializable]
 45[JsonConverter(typeof(StateJsonConverterFactory))]
 46[MemoryPackable]
 47public sealed partial class SwiftSparseMap<T> : IStateBacked<SwiftSparseSetState<T>>, ISwiftCloneable<T>, IEnumerable<Ke
 48{
 49    #region Constants
 50
 51    /// <summary>
 52    /// Represents the default initial capacity for dense collections.
 53    /// </summary>
 54    public const int DefaultDenseCapacity = 8;
 55
 56    /// <summary>
 57    /// Represents the default initial capacity for sparse collections.
 58    /// </summary>
 59    public const int DefaultSparseCapacity = 8;
 60
 61    /// <summary>
 62    /// Represents the value used to indicate that a key is not present in the sparse array.
 63    /// </summary>
 64    /// <remarks>
 65    /// A value of 0 signifies that the key is absent.
 66    /// When a key is present, the stored value is the dense index plus one.
 67    /// </remarks>
 68    private const int NotPresent = 0;
 69
 70    #endregion
 71
 72    #region Fields
 73
 74    private int[] _sparse;       // key -> denseIndex+1
 75    private int[] _denseKeys;    // denseIndex -> key
 76    private T[] _denseValues;    // denseIndex -> value
 77    private int _count;
 78
 79    [NonSerialized]
 80    private uint _version;
 81
 82    [NonSerialized]
 83    private object? _syncRoot;
 84
 85    #endregion
 86
 87    #region Constructors
 88
 89    /// <summary>
 90    /// Initializes a new instance of the SwiftSparseMap class with default sparse and dense capacities.
 91    /// </summary>
 6292    public SwiftSparseMap() : this(DefaultSparseCapacity, DefaultDenseCapacity) { }
 93
 94    /// <summary>
 95    /// Initializes a new sparse map with the specified sparse and dense capacities.
 96    /// </summary>
 97    /// <param name="sparseCapacity">
 98    /// Initial sparse lookup capacity. This should track the highest expected key plus one,
 99    /// not the number of stored values.
 100    /// </param>
 101    /// <param name="denseCapacity">Initial dense storage capacity for values.</param>
 32102    public SwiftSparseMap(int sparseCapacity, int denseCapacity)
 103    {
 32104        SwiftThrowHelper.ThrowIfNegative(sparseCapacity, nameof(sparseCapacity));
 32105        SwiftThrowHelper.ThrowIfNegative(denseCapacity, nameof(denseCapacity));
 106
 32107        int sparseSize = sparseCapacity == 0 ? 0 : SwiftHashTools.NextPowerOfTwo(sparseCapacity);
 32108        _sparse = sparseCapacity == 0
 32109            ? Array.Empty<int>()
 32110            : new int[sparseSize];
 32111        int denseSize = denseCapacity < DefaultDenseCapacity
 32112            ? DefaultDenseCapacity
 32113            : SwiftHashTools.NextPowerOfTwo(denseCapacity);
 32114        _denseKeys = denseCapacity == 0
 32115            ? Array.Empty<int>()
 32116            : new int[denseSize];
 32117        _denseValues = _denseKeys.Length == 0 ? Array.Empty<T>() : new T[_denseKeys.Length];
 118
 32119        _count = 0;
 32120    }
 121
 122    /// <summary>
 123    /// Initializes a new instance of the SwiftSparseMap class using the specified state.
 124    /// </summary>
 125    /// <param name="state">The state object that provides the initial configuration and data for the map. Cannot be nul
 126    [MemoryPackConstructor]
 6127    public SwiftSparseMap(SwiftSparseSetState<T> state)
 128    {
 6129        State = state;
 130
 4131        _sparse ??= new int[DefaultSparseCapacity];
 4132        _denseKeys ??= new int[DefaultDenseCapacity];
 4133        _denseValues ??= new T[DefaultDenseCapacity];
 4134    }
 135
 136    #endregion
 137
 138    #region Properties
 139
 140    /// <summary>
 141    /// Gets the number of elements contained in the collection.
 142    /// </summary>
 143    [JsonIgnore]
 144    [MemoryPackIgnore]
 14145    public int Count => _count;
 146
 147    /// <summary>
 148    /// Capacity of the dense arrays (Keys/Values storage).
 149    /// </summary>
 150    [JsonIgnore]
 151    [MemoryPackIgnore]
 2152    public int DenseCapacity => _denseKeys.Length;
 153
 154    /// <summary>
 155    /// Capacity of the sparse array (max key+1 that can be mapped without resizing).
 156    /// Memory usage grows with this capacity.
 157    /// </summary>
 158    [JsonIgnore]
 159    [MemoryPackIgnore]
 3160    public int SparseCapacity => _sparse.Length;
 161
 162    /// <summary>
 163    /// Gets a value indicating whether access to the collection is synchronized (thread safe).
 164    /// </summary>
 165    [JsonIgnore]
 166    [MemoryPackIgnore]
 1167    public bool IsSynchronized => false;
 168
 169    /// <summary>
 170    /// Gets an object that can be used to synchronize access to the collection.
 171    /// </summary>
 172    /// <remarks>
 173    /// Use this object to lock the collection during multithreaded operations to ensure thread safety.
 174    /// The returned object is unique to this collection instance.
 175    /// </remarks>
 176    [JsonIgnore]
 177    [MemoryPackIgnore]
 1178    public object SyncRoot => _syncRoot ??= new object();
 179
 180    /// <summary>
 181    /// Returns the dense keys array (valid range: [0..Count)).
 182    /// </summary>
 183    [JsonIgnore]
 184    [MemoryPackIgnore]
 5185    public int[] DenseKeys => _denseKeys;
 186
 187    /// <summary>
 188    /// Gets a span containing the keys currently stored in the collection.
 189    /// </summary>
 190    /// <remarks>
 191    /// The returned span provides a view of the underlying key data and reflects the current state of the collection.
 192    /// Modifying the span will affect the collection's contents.
 193    /// The span is only valid as long as the underlying collection is not modified.
 194    /// </remarks>
 195    [JsonIgnore]
 196    [MemoryPackIgnore]
 1197    public Span<int> Keys => _denseKeys.AsSpan(0, _count);
 198
 199    /// <summary>
 200    /// Returns the dense values array (valid range: [0..Count)).
 201    /// </summary>
 202    [JsonIgnore]
 203    [MemoryPackIgnore]
 4204    public T[] DenseValues => _denseValues;
 205
 206    /// <summary>
 207    /// Gets a span containing the current values in the collection.
 208    /// </summary>
 209    /// <remarks>
 210    /// The returned span reflects the live contents of the collection up to the current count.
 211    /// Modifying the span will update the underlying collection data.
 212    /// The span length is equal to the number of elements currently stored.
 213    /// </remarks>
 214    [JsonIgnore]
 215    [MemoryPackIgnore]
 1216    public Span<T> Values => _denseValues.AsSpan(0, _count);
 217
 218    /// <summary>
 219    /// Gets/sets the value for a key. Setting:
 220    /// - overwrites if present
 221    /// - inserts if not present
 222    /// </summary>
 223    [JsonIgnore]
 224    [MemoryPackIgnore]
 225    public T this[int key]
 226    {
 227        get
 228        {
 20229            int denseIndex = GetDenseIndexOrThrow(key);
 17230            return _denseValues[denseIndex];
 231        }
 232        set
 233        {
 40234            EnsureSparseCapacity(GetRequiredSparseCapacity(key));
 235
 38236            int slot = _sparse[key];
 38237            if (slot != NotPresent)
 238            {
 239                // present -> overwrite
 1240                int denseIndex = slot - 1;
 1241                _denseValues[denseIndex] = value;
 1242                _version++;
 1243                return;
 244            }
 245
 246            // not present -> insert
 37247            EnsureDenseCapacity(_count + 1);
 248
 37249            int newIndex = _count++;
 37250            _denseKeys[newIndex] = key;
 37251            _denseValues[newIndex] = value;
 37252            _sparse[key] = newIndex + 1;
 253
 37254            _version++;
 37255        }
 256    }
 257
 258    /// <summary>
 259    /// Gets or sets the current state of the sparse set, including the used dense keys and values.
 260    /// </summary>
 261    /// <remarks>
 262    /// The state includes only the active elements in the set.
 263    /// Setting this property replaces the current contents with the provided state.
 264    /// The setter is intended for internal use, such as serialization or deserialization scenarios.
 265    /// </remarks>
 266    [JsonInclude]
 267    [MemoryPackInclude]
 268    public SwiftSparseSetState<T> State
 269    {
 270        get
 271        {
 272            // Serialize only the used portions of dense arrays
 4273            var denseKeys = new int[_count];
 4274            Array.Copy(_denseKeys, denseKeys, _count);
 275
 4276            var denseValues = new T[_count];
 4277            Array.Copy(_denseValues, denseValues, _count);
 278
 4279            return new SwiftSparseSetState<T>(denseKeys, denseValues);
 280        }
 281        internal set
 282        {
 6283            SwiftThrowHelper.ThrowIfNull(value.DenseKeys);
 6284            SwiftThrowHelper.ThrowIfNull(value.DenseValues);
 285
 6286            int n = value.DenseKeys.Length;
 287
 6288            SwiftThrowHelper.ThrowIfArgument(n != value.DenseValues.Length, nameof(value), "DenseKeys and DenseValues le
 289
 290            // Allocate dense storage
 5291            _denseKeys = n == 0 ? Array.Empty<int>() : new int[Math.Max(DefaultDenseCapacity, n)];
 5292            _denseValues = n == 0 ? Array.Empty<T>() : new T[_denseKeys.Length];
 293
 5294            if (n > 0)
 295            {
 5296                Array.Copy(value.DenseKeys, _denseKeys, n);
 5297                Array.Copy(value.DenseValues, _denseValues, n);
 298            }
 299
 5300            _count = n;
 301
 302            // Compute maxKey from dense keys
 5303            int maxKey = -1;
 28304            for (int i = 0; i < n; i++)
 305            {
 9306                int key = _denseKeys[i];
 9307                SwiftThrowHelper.ThrowIfArgument(key < 0, nameof(value), "Key cannot be negative.");
 308
 9309                if (key > maxKey)
 8310                    maxKey = key;
 311            }
 312
 313            // Allocate sparse map
 5314            int sparseSize = maxKey < 0
 5315                ? DefaultSparseCapacity
 5316                : Math.Max(DefaultSparseCapacity, GetRequiredSparseCapacity(maxKey));
 5317            _sparse = new int[sparseSize];
 318
 319            // Rebuild sparse lookup
 26320            for (int i = 0; i < n; i++)
 321            {
 9322                int key = _denseKeys[i];
 323
 9324                SwiftThrowHelper.ThrowIfArgument(_sparse[key] != NotPresent, nameof(value), "Duplicate key in DenseKeys.
 325
 8326                _sparse[key] = i + 1;
 327            }
 328
 4329            _version++;
 4330        }
 331    }
 332
 333    #endregion
 334
 335    #region Core Operations
 336
 337    /// <summary>
 338    /// Determines whether the collection contains the specified key.
 339    /// </summary>
 340    /// <param name="key">The key to locate in the collection.</param>
 341    /// <returns>true if the collection contains an element with the specified key; otherwise, false.</returns>
 342    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 343    public bool ContainsKey(int key)
 344    {
 12345        if ((uint)key >= (uint)_sparse.Length) return false;
 10346        return _sparse[key] != NotPresent;
 347    }
 348
 349    /// <summary>
 350    /// Adds a key/value only if the key is not present.
 351    /// Returns false if already present.
 352    /// </summary>
 353    public bool TryAdd(int key, T value)
 354    {
 2355        EnsureSparseCapacity(GetRequiredSparseCapacity(key));
 2356        if (_sparse[key] != NotPresent)
 1357            return false;
 358
 1359        EnsureDenseCapacity(_count + 1);
 360
 1361        int newIndex = _count++;
 1362        _denseKeys[newIndex] = key;
 1363        _denseValues[newIndex] = value;
 1364        _sparse[key] = newIndex + 1;
 365
 1366        _version++;
 1367        return true;
 368    }
 369
 370    /// <summary>
 371    /// Adds or overwrites (same behavior as indexer set).
 372    /// </summary>
 39373    public void Add(int key, T value) => this[key] = value;
 374
 375    /// <summary>
 376    /// Attempts to retrieve the value associated with the specified key.
 377    /// </summary>
 378    /// <param name="key">The key whose associated value is to be retrieved.</param>
 379    /// <param name="value">
 380    /// When this method returns, contains the value associated with the specified key, if the key is found;
 381    /// otherwise, the default value for the type parameter <typeparamref name="T"/>.
 382    /// This parameter is passed uninitialized.
 383    /// </param>
 384    /// <returns>true if the key was found and its value was retrieved; otherwise, false.</returns>
 385    public bool TryGetValue(int key, out T value)
 386    {
 2387        if ((uint)key < (uint)_sparse.Length)
 388        {
 1389            int slot = _sparse[key];
 1390            if (slot != NotPresent)
 391            {
 1392                value = _denseValues[slot - 1];
 1393                return true;
 394            }
 395        }
 396
 1397        value = default!;
 1398        return false;
 399    }
 400
 401    /// <summary>
 402    /// Removes the element with the specified key from the collection, if it exists.
 403    /// </summary>
 404    /// <param name="key">The key of the element to remove. Must be a non-negative integer within the valid range of key
 405    /// <returns>true if the element is successfully found and removed; otherwise, false.</returns>
 406    public bool Remove(int key)
 407    {
 5408        if ((uint)key >= (uint)_sparse.Length) return false;
 409
 3410        int slot = _sparse[key];
 3411        if (slot == NotPresent) return false;
 412
 3413        int index = slot - 1;
 3414        int last = --_count;
 415
 3416        _sparse[key] = NotPresent;
 417
 3418        if (index != last)
 419        {
 1420            int movedKey = _denseKeys[last];
 421
 1422            _denseKeys[index] = movedKey;
 1423            _denseValues[index] = _denseValues[last];
 424
 1425            _sparse[movedKey] = index + 1;
 426        }
 427
 3428        _denseKeys[last] = default;
 3429        _denseValues[last] = default!;
 430
 3431        _version++;
 3432        return true;
 433    }
 434
 435    /// <summary>
 436    /// Removes all keys and values from the collection.
 437    /// </summary>
 438    /// <remarks>
 439    /// After calling this method, the collection will be empty and its Count property will be zero.
 440    /// This method does not reduce the capacity of the underlying storage.
 441    /// </remarks>
 442    public void Clear()
 443    {
 7444        if (_count == 0) return;
 445
 446        // reset sparse for keys that were present
 14447        for (int i = 0; i < _count; i++)
 448        {
 4449            int key = _denseKeys[i];
 4450            if ((uint)key < (uint)_sparse.Length)
 4451                _sparse[key] = NotPresent;
 452        }
 453
 3454        Array.Clear(_denseKeys, 0, _count);
 3455        Array.Clear(_denseValues, 0, _count);
 456
 3457        _count = 0;
 3458        _version++;
 3459    }
 460
 461    #endregion
 462
 463    #region Capacity Management
 464
 465    /// <summary>
 466    /// Ensures that the internal dense storage has at least the specified capacity, expanding it if necessary.
 467    /// </summary>
 468    /// <remarks>
 469    /// If the current capacity is less than the specified value, the internal storage is resized to accommodate at leas
 470    /// Existing elements are preserved.
 471    /// The capacity is increased to the next power of two greater than or equal to the requested capacity for performan
 472    /// </remarks>
 473    /// <param name="capacity">The minimum number of elements that the dense storage must be able to hold. Must be non-n
 474    public void EnsureDenseCapacity(int capacity)
 475    {
 77476        if (capacity <= _denseKeys.Length) return;
 477
 1478        int newCap = _denseKeys.Length == 0 ? DefaultDenseCapacity : _denseKeys.Length * 2;
 2479        if (newCap < capacity) newCap = capacity;
 480
 1481        newCap = SwiftHashTools.NextPowerOfTwo(newCap);
 482
 1483        var newKeys = new int[newCap];
 1484        var newVals = new T[newCap];
 485
 1486        if (_count > 0)
 487        {
 0488            Array.Copy(_denseKeys, newKeys, _count);
 0489            Array.Copy(_denseValues, newVals, _count);
 490        }
 491
 1492        _denseKeys = newKeys;
 1493        _denseValues = newVals;
 494
 1495        _version++;
 1496    }
 497
 498    /// <summary>
 499    /// Ensures that the internal sparse array has a capacity at least as large as the specified value.
 500    /// </summary>
 501    /// <remarks>
 502    /// If the current capacity is less than the specified value, the internal storage is resized to accommodate at leas
 503    /// Existing elements are preserved.
 504    /// The capacity is increased to the next power of two greater than or equal to the requested capacity for performan
 505    /// </remarks>
 506    /// <param name="capacity">The minimum required capacity for the internal sparse array. Must be non-negative.</param
 507    public void EnsureSparseCapacity(int capacity)
 508    {
 78509        if (capacity <= _sparse.Length) return;
 510
 4511        int newCap = _sparse.Length == 0
 4512            ? DefaultSparseCapacity
 4513            : _sparse.Length * 2;
 7514        if (newCap < capacity) newCap = capacity;
 515
 4516        newCap = SwiftHashTools.NextPowerOfTwo(newCap);
 517
 4518        var newSparse = new int[newCap];
 4519        if (_sparse.Length > 0)
 4520            Array.Copy(_sparse, newSparse, _sparse.Length);
 521
 4522        _sparse = newSparse;
 4523        _version++;
 4524    }
 525
 526    /// <summary>
 527    /// Reduces the memory usage of the collection by resizing internal storage to fit the current number of elements as
 528    /// </summary>
 529    /// <remarks>
 530    /// Call this method to minimize the collection's memory footprint after removing a significant number of elements.
 531    /// This operation may improve memory efficiency but can be an expensive operation if the collection is large.
 532    /// The method does not affect the logical contents of the collection.
 533    /// </remarks>
 534    public void TrimExcess()
 535    {
 536        // Dense: shrink to Count (with a minimum)
 1537        int newDense = Math.Max(DefaultDenseCapacity, _count);
 1538        if (newDense < _denseKeys.Length)
 539        {
 1540            var newKeys = new int[newDense];
 1541            var newVals = new T[newDense];
 1542            if (_count > 0)
 543            {
 1544                Array.Copy(_denseKeys, newKeys, _count);
 1545                Array.Copy(_denseValues, newVals, _count);
 546            }
 1547            _denseKeys = newKeys;
 1548            _denseValues = newVals;
 549        }
 550
 551        // Sparse: shrink to (maxKey+1) based on dense keys
 1552        int maxKey = -1;
 4553        for (int i = 0; i < _count; i++)
 2554            if (_denseKeys[i] > maxKey) maxKey = _denseKeys[i];
 555
 1556        int newSparse = maxKey < 0
 1557            ? DefaultSparseCapacity
 1558            : Math.Max(DefaultSparseCapacity, GetRequiredSparseCapacity(maxKey));
 1559        if (newSparse < _sparse.Length)
 560        {
 1561            var newMap = new int[newSparse];
 562            // rebuild from dense
 4563            for (int i = 0; i < _count; i++)
 1564                newMap[_denseKeys[i]] = i + 1;
 1565            _sparse = newMap;
 566        }
 567
 1568        _version++;
 1569    }
 570
 571    #endregion
 572
 573    #region Enumeration
 574
 575    /// <inheritdoc cref="IEnumerable.GetEnumerator()"/>
 8576    public SwiftSparseMapEnumerator GetEnumerator() => new(this);
 5577    IEnumerator<KeyValuePair<int, T>> IEnumerable<KeyValuePair<int, T>>.GetEnumerator() => GetEnumerator();
 1578    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 579
 580    /// <summary>
 581    /// Supports iteration over the key/value pairs in a <see cref="SwiftSparseMap{T}"/> collection.
 582    /// </summary>
 583    /// <remarks>
 584    /// The enumerator provides a forward-only, read-only traversal of the collection.
 585    /// It is invalidated if the underlying collection is modified during enumeration, and any such modification will ca
 586    /// subsequent operations to throw an InvalidOperationException.
 587    /// </remarks>
 588    public struct SwiftSparseMapEnumerator : IEnumerator<KeyValuePair<int, T>>
 589    {
 590        private readonly SwiftSparseMap<T> _set;
 591        private readonly int[] _keys;
 592        private readonly T[] _values;
 593        private readonly int _count;
 594        private readonly uint _version;
 595        private int _index;
 596
 597        internal SwiftSparseMapEnumerator(SwiftSparseMap<T> set)
 598        {
 8599            _set = set;
 8600            _keys = set._denseKeys;
 8601            _values = set._denseValues;
 8602            _count = set._count;
 8603            _version = set._version;
 8604            _index = -1;
 8605            Current = default;
 8606        }
 607
 608        /// <inheritdoc/>
 24609        public KeyValuePair<int, T> Current { get; private set; }
 1610        object IEnumerator.Current => Current;
 611
 612        /// <inheritdoc/>
 613        public bool MoveNext()
 614        {
 11615            SwiftThrowHelper.ThrowIfTrue(_version != _set._version, message: "Collection was modified during enumeration
 616
 10617            int next = _index + 1;
 10618            if (next >= _count)
 619            {
 5620                Current = default;
 5621                return false;
 622            }
 623
 5624            _index = next;
 5625            Current = new KeyValuePair<int, T>(_keys[_index], _values[_index]);
 5626            return true;
 627        }
 628
 629        /// <inheritdoc/>
 630        public void Reset()
 631        {
 1632            SwiftThrowHelper.ThrowIfTrue(_version != _set._version, message: "Collection was modified during enumeration
 633
 1634            _index = -1;
 1635            Current = default;
 1636        }
 637
 638        /// <inheritdoc/>
 5639        public void Dispose() => _index = -1;
 640    }
 641
 642    #endregion
 643
 644    #region Helpers
 645
 646    /// <summary>
 647    /// Retrieves the dense representation of the collection as parallel arrays of keys and values, along with the numbe
 648    /// </summary>
 649    /// <remarks>
 650    /// The arrays returned may be larger than the actual number of elements.
 651    /// Only the first <paramref name="count"/> entries in each array are valid and should be used.
 652    /// </remarks>
 653    /// <param name="keys">
 654    /// When this method returns, contains an array of keys representing the dense mapping.
 655    /// The array length is at least as large as the number of elements returned in <paramref name="count"/>.
 656    /// </param>
 657    /// <param name="values">
 658    /// When this method returns, contains an array of values corresponding to the keys in <paramref name="keys"/>.
 659    /// The array length is at least as large as the number of elements returned in <paramref name="count"/>.
 660    /// </param>
 661    /// <param name="count">When this method returns, contains the number of valid key-value pairs in the dense arrays.<
 662    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 663    public void GetDense(out int[] keys, out T[] values, out int count)
 664    {
 1665        keys = _denseKeys;
 1666        values = _denseValues;
 1667        count = _count;
 1668    }
 669
 670    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 671    private static int GetRequiredSparseCapacity(int key)
 672    {
 48673        SwiftThrowHelper.ThrowIfNegative(key, nameof(key));
 47674        SwiftThrowHelper.ThrowIfArgumentOutOfRange(key == int.MaxValue, key, nameof(key), "Key is too large for direct s
 675
 46676        return key + 1;
 677    }
 678
 679    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 680    private int GetDenseIndexOrThrow(int key)
 681    {
 20682        SwiftThrowHelper.ThrowIfKeyNotFound((uint)key >= (uint)_sparse.Length, key);
 683
 18684        int slot = _sparse[key];
 685
 18686        SwiftThrowHelper.ThrowIfKeyNotFound(slot == NotPresent, key);
 687
 17688        return slot - 1;
 689    }
 690
 691    /// <inheritdoc/>
 692    public void CloneTo(ICollection<T> output)
 693    {
 1694        SwiftThrowHelper.ThrowIfNull(output, nameof(output));
 695
 1696        output.Clear();
 697
 6698        for (int i = 0; i < _count; i++)
 2699            output.Add(_denseValues[i]);
 1700    }
 701
 702    #endregion
 703}