< Summary

Information
Class: SwiftCollections.SwiftSparseSet
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftSparseSet.cs
Line coverage
100%
Covered lines: 310
Uncovered lines: 0
Coverable lines: 310
Total lines: 839
Line coverage: 100%
Branch coverage
98%
Covered branches: 170
Total branches: 172
Branch coverage: 98.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%11100%
.ctor(...)100%88100%
.ctor(...)100%11100%
get_Count()100%11100%
get_DenseCapacity()100%11100%
get_SparseCapacity()100%11100%
get_IsReadOnly()100%11100%
get_IsSynchronized()100%11100%
get_SyncRoot()50%22100%
get_DenseKeys()100%11100%
get_Keys()100%11100%
get_State()100%11100%
set_State(...)100%11100%
RestoreDenseKeys(...)100%44100%
ValidateDenseKeys(...)100%44100%
RestoreSparseLookup(...)100%44100%
Contains(...)100%22100%
ContainsKey(...)100%11100%
Add(...)100%22100%
TryAdd(...)100%11100%
System.Collections.Generic.ICollection<System.Int32>.Add(...)100%11100%
Remove(...)100%66100%
Clear()100%44100%
ExceptWith(...)100%66100%
IntersectWith(...)100%88100%
IsProperSubsetOf(...)100%1010100%
IsProperSupersetOf(...)100%1010100%
IsSubsetOf(...)100%88100%
IsSupersetOf(...)100%66100%
Overlaps(...)100%44100%
SetEquals(...)100%66100%
SymmetricExceptWith(...)100%66100%
UnionWith(...)100%22100%
RemoveWhereMissingFrom(...)100%44100%
RemoveWhereMissingFrom(...)100%44100%
AllKeysIn(...)100%44100%
AllKeysIn(...)100%44100%
IsSubsetOfSparseSet(...)100%22100%
IsSubsetOfSet(...)100%22100%
SetEqualsSparseSet(...)100%22100%
SetEqualsSet(...)100%22100%
SymmetricExceptWithSet(...)100%44100%
EnsureDenseCapacity(...)100%88100%
EnsureSparseCapacity(...)100%88100%
TrimExcess()100%11100%
TrimDenseStorage()100%44100%
TrimSparseLookup()90%1010100%
AsReadOnlySpan()100%11100%
GetDense(...)100%11100%
CopyKeysTo(...)100%11100%
CopySortedKeysTo(...)100%11100%
CopyTo(...)100%11100%
System.Collections.ICollection.CopyTo(...)100%88100%
CloneTo(...)100%22100%
GetEnumerator()100%11100%
System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()100%11100%
System.Collections.IEnumerable.GetEnumerator()100%11100%
.ctor(...)100%11100%
System.Collections.IEnumerator.get_Current()100%11100%
MoveNext()100%22100%
Reset()100%11100%
Dispose()100%11100%
GetRequiredSparseCapacity(...)100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// SwiftSparseSet.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 sparse set for externally supplied non-negative integer IDs.
 22/// Provides O(1) Add, Remove, Contains, and densely packed iteration.
 23/// </summary>
 24/// <remarks>
 25/// <para>
 26/// <see cref="SwiftSparseSet"/> is intended for membership workloads where the caller already owns
 27/// compact integer IDs, such as entity handles, body IDs, or slot indices.
 28/// </para>
 29/// <para>
 30/// Internally, IDs are stored in a dense array for cache-friendly iteration while a sparse lookup
 31/// table maps each ID directly to its dense position. Removal uses swap-back, so iteration order is
 32/// not stable.
 33/// </para>
 34/// <para>
 35/// Memory usage scales with the highest stored ID rather than only the number of IDs. For arbitrary,
 36/// huge, or widely spaced keys, prefer <see cref="SwiftHashSet{T}"/> with <c>int</c> keys.
 37/// </para>
 38/// </remarks>
 39[Serializable]
 40[JsonConverter(typeof(StateJsonConverterFactory))]
 41[MemoryPackable]
 42public sealed partial class SwiftSparseSet : IStateBacked<SwiftArrayState<int>>, ISwiftCloneable<int>, ISet<int>, IReadO
 43{
 44    #region Constants
 45
 46    /// <summary>
 47    /// Represents the default initial capacity for dense ID storage.
 48    /// </summary>
 49    public const int DefaultDenseCapacity = 8;
 50
 51    /// <summary>
 52    /// Represents the default initial capacity for sparse ID lookup.
 53    /// </summary>
 54    public const int DefaultSparseCapacity = 8;
 55
 56    private const int NotPresent = 0;
 57
 58    #endregion
 59
 60    #region Fields
 61
 62    private int[] _sparse;       // id -> denseIndex+1
 63    private int[] _denseKeys;    // denseIndex -> id
 64    private int _count;
 65
 66    [NonSerialized]
 67    private uint _version;
 68
 69    [NonSerialized]
 70    private object? _syncRoot;
 71
 72    #endregion
 73
 74    #region Constructors
 75
 76    /// <summary>
 77    /// Initializes a new instance of the <see cref="SwiftSparseSet"/> class with default sparse and dense capacities.
 78    /// </summary>
 8079    public SwiftSparseSet() : this(DefaultSparseCapacity, DefaultDenseCapacity) { }
 80
 81    /// <summary>
 82    /// Initializes a new instance of the <see cref="SwiftSparseSet"/> class with matching sparse and dense capacities.
 83    /// </summary>
 84    /// <param name="capacity">The initial sparse and dense capacity.</param>
 285    public SwiftSparseSet(int capacity) : this(capacity, capacity) { }
 86
 87    /// <summary>
 88    /// Initializes a new instance of the <see cref="SwiftSparseSet"/> class with explicit sparse and dense capacities.
 89    /// </summary>
 90    /// <param name="sparseCapacity">
 91    /// Initial sparse lookup capacity. This should track the highest expected ID plus one,
 92    /// not just the number of stored IDs.
 93    /// </param>
 94    /// <param name="denseCapacity">Initial dense storage capacity for IDs.</param>
 4495    public SwiftSparseSet(int sparseCapacity, int denseCapacity)
 96    {
 4497        SwiftThrowHelper.ThrowIfNegative(sparseCapacity, nameof(sparseCapacity));
 4498        SwiftThrowHelper.ThrowIfNegative(denseCapacity, nameof(denseCapacity));
 99
 44100        int sparseSize = sparseCapacity == 0 ? 0 : SwiftHashTools.NextPowerOfTwo(sparseCapacity);
 44101        _sparse = sparseCapacity == 0
 44102            ? Array.Empty<int>()
 44103            : new int[sparseSize];
 104
 44105        int denseSize = denseCapacity < DefaultDenseCapacity
 44106            ? DefaultDenseCapacity
 44107            : SwiftHashTools.NextPowerOfTwo(denseCapacity);
 44108        _denseKeys = denseCapacity == 0
 44109            ? Array.Empty<int>()
 44110            : new int[denseSize];
 44111    }
 112
 113    /// <summary>
 114    /// Initializes a new instance of the <see cref="SwiftSparseSet"/> class using the specified state.
 115    /// </summary>
 116    /// <param name="state">The state object that provides the initial IDs. Cannot be null.</param>
 117    [MemoryPackConstructor]
 5118    public SwiftSparseSet(SwiftArrayState<int> state)
 119    {
 5120        _sparse = Array.Empty<int>();
 5121        _denseKeys = Array.Empty<int>();
 122
 5123        State = state;
 3124    }
 125
 126    #endregion
 127
 128    #region Properties
 129
 130    /// <summary>
 131    /// Gets the number of IDs contained in the set.
 132    /// </summary>
 133    [JsonIgnore]
 134    [MemoryPackIgnore]
 4135    public int Count => _count;
 136
 137    /// <summary>
 138    /// Capacity of the dense ID storage.
 139    /// </summary>
 140    [JsonIgnore]
 141    [MemoryPackIgnore]
 7142    public int DenseCapacity => _denseKeys.Length;
 143
 144    /// <summary>
 145    /// Capacity of the sparse lookup table.
 146    /// </summary>
 147    [JsonIgnore]
 148    [MemoryPackIgnore]
 7149    public int SparseCapacity => _sparse.Length;
 150
 151    /// <inheritdoc/>
 152    [JsonIgnore]
 153    [MemoryPackIgnore]
 1154    public bool IsReadOnly => false;
 155
 156    /// <summary>
 157    /// Gets a value indicating whether access to the collection is synchronized.
 158    /// </summary>
 159    [JsonIgnore]
 160    [MemoryPackIgnore]
 1161    public bool IsSynchronized => false;
 162
 163    /// <summary>
 164    /// Gets an object that can be used to synchronize access to the collection.
 165    /// </summary>
 166    [JsonIgnore]
 167    [MemoryPackIgnore]
 1168    public object SyncRoot => _syncRoot ??= new object();
 169
 170    /// <summary>
 171    /// Returns the dense ID array. Only the range [0..Count) is populated.
 172    /// </summary>
 173    [JsonIgnore]
 174    [MemoryPackIgnore]
 4175    public int[] DenseKeys => _denseKeys;
 176
 177    /// <summary>
 178    /// Gets a span containing the current IDs in dense iteration order.
 179    /// </summary>
 180    [JsonIgnore]
 181    [MemoryPackIgnore]
 3182    public Span<int> Keys => _denseKeys.AsSpan(0, _count);
 183
 184    /// <summary>
 185    /// Gets or sets the current state of the sparse set.
 186    /// </summary>
 187    [JsonInclude]
 188    [MemoryPackInclude]
 189    public SwiftArrayState<int> State
 190    {
 191        get
 192        {
 2193            var items = new int[_count];
 2194            Array.Copy(_denseKeys, items, _count);
 2195            return new SwiftArrayState<int>(items);
 196        }
 197        internal set
 198        {
 5199            SwiftThrowHelper.ThrowIfNull(value.Items, nameof(value.Items));
 200
 5201            RestoreDenseKeys(value.Items);
 5202            int maxKey = ValidateDenseKeys(nameof(value.Items));
 4203            RestoreSparseLookup(maxKey, nameof(value));
 204
 3205            _version++;
 3206        }
 207    }
 208
 209    private void RestoreDenseKeys(int[] items)
 210    {
 5211        int count = items.Length;
 5212        _denseKeys = count == 0
 5213            ? Array.Empty<int>()
 5214            : new int[Math.Max(DefaultDenseCapacity, SwiftHashTools.NextPowerOfTwo(count))];
 215
 5216        if (count > 0)
 4217            Array.Copy(items, _denseKeys, count);
 218
 5219        _count = count;
 5220    }
 221
 222    private int ValidateDenseKeys(string paramName)
 223    {
 5224        int maxKey = -1;
 20225        for (int i = 0; i < _count; i++)
 226        {
 6227            int key = _denseKeys[i];
 6228            SwiftThrowHelper.ThrowIfNegative(key, paramName);
 5229            SwiftThrowHelper.ThrowIfArgumentOutOfRange(key == int.MaxValue, key, paramName, "ID is too large for direct 
 230
 5231            if (key > maxKey)
 4232                maxKey = key;
 233        }
 234
 4235        return maxKey;
 236    }
 237
 238    private void RestoreSparseLookup(int maxKey, string paramName)
 239    {
 4240        int sparseSize = maxKey < 0
 4241            ? DefaultSparseCapacity
 4242            : Math.Max(DefaultSparseCapacity, GetRequiredSparseCapacity(maxKey));
 4243        _sparse = new int[sparseSize];
 244
 16245        for (int i = 0; i < _count; i++)
 246        {
 5247            int key = _denseKeys[i];
 5248            SwiftThrowHelper.ThrowIfArgument(_sparse[key] != NotPresent, paramName, "Duplicate ID in sparse set state.")
 4249            _sparse[key] = i + 1;
 250        }
 3251    }
 252
 253    #endregion
 254
 255    #region Core Operations
 256
 257    /// <summary>
 258    /// Determines whether the set contains the specified ID.
 259    /// </summary>
 260    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 261    public bool Contains(int item)
 262    {
 50263        if ((uint)item >= (uint)_sparse.Length) return false;
 40264        return _sparse[item] != NotPresent;
 265    }
 266
 267    /// <summary>
 268    /// Determines whether the set contains the specified key. Alias for <see cref="Contains(int)"/>.
 269    /// </summary>
 270    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 5271    public bool ContainsKey(int key) => Contains(key);
 272
 273    /// <summary>
 274    /// Adds the specified ID if it is not already present.
 275    /// </summary>
 276    /// <returns>true if the ID was added; false if it was already present.</returns>
 277    public bool Add(int item)
 278    {
 90279        EnsureSparseCapacity(GetRequiredSparseCapacity(item));
 88280        if (_sparse[item] != NotPresent)
 1281            return false;
 282
 87283        EnsureDenseCapacity(_count + 1);
 284
 87285        int newIndex = _count++;
 87286        _denseKeys[newIndex] = item;
 87287        _sparse[item] = newIndex + 1;
 288
 87289        _version++;
 87290        return true;
 291    }
 292
 293    /// <summary>
 294    /// Adds the specified ID if it is not already present.
 295    /// </summary>
 1296    public bool TryAdd(int item) => Add(item);
 297
 1298    void ICollection<int>.Add(int item) => Add(item);
 299
 300    /// <summary>
 301    /// Removes the specified ID from the set.
 302    /// </summary>
 303    /// <returns>true if the ID was found and removed; otherwise, false.</returns>
 304    public bool Remove(int item)
 305    {
 24306        if ((uint)item >= (uint)_sparse.Length) return false;
 307
 18308        int slot = _sparse[item];
 22309        if (slot == NotPresent) return false;
 310
 14311        int index = slot - 1;
 14312        int last = --_count;
 313
 14314        _sparse[item] = NotPresent;
 315
 14316        if (index != last)
 317        {
 12318            int movedKey = _denseKeys[last];
 12319            _denseKeys[index] = movedKey;
 12320            _sparse[movedKey] = index + 1;
 321        }
 322
 14323        _denseKeys[last] = default;
 14324        _version++;
 14325        return true;
 326    }
 327
 328    /// <summary>
 329    /// Removes all IDs from the set without reducing capacity.
 330    /// </summary>
 331    public void Clear()
 332    {
 5333        if (_count == 0) return;
 334
 18335        for (int i = 0; i < _count; i++)
 336        {
 6337            int key = _denseKeys[i];
 6338            _sparse[key] = NotPresent;
 6339            _denseKeys[i] = default;
 340        }
 341
 3342        _count = 0;
 3343        _version++;
 3344    }
 345
 346    #endregion
 347
 348    #region Set Operations
 349
 350    /// <inheritdoc/>
 351    public void ExceptWith(IEnumerable<int> other)
 352    {
 3353        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 354
 4355        if (_count == 0) return;
 2356        if (ReferenceEquals(other, this))
 357        {
 1358            Clear();
 1359            return;
 360        }
 361
 6362        foreach (int item in other)
 2363            Remove(item);
 1364    }
 365
 366    /// <inheritdoc/>
 367    public void IntersectWith(IEnumerable<int> other)
 368    {
 5369        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 370
 7371        if (_count == 0 || ReferenceEquals(other, this)) return;
 372
 3373        if (other is SwiftSparseSet sparseSet)
 374        {
 1375            RemoveWhereMissingFrom(sparseSet);
 1376            return;
 377        }
 378
 2379        if (other is ISet<int> set)
 380        {
 1381            RemoveWhereMissingFrom(set);
 1382            return;
 383        }
 384
 1385        RemoveWhereMissingFrom(new HashSet<int>(other));
 1386    }
 387
 388    /// <inheritdoc/>
 389    public bool IsProperSubsetOf(IEnumerable<int> other)
 390    {
 6391        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 392
 6393        if (other is SwiftSparseSet sparseSet)
 2394            return _count < sparseSet._count && IsSubsetOf(sparseSet);
 395
 4396        if (other is ISet<int> set)
 2397            return _count < set.Count && IsSubsetOf(set);
 398
 2399        var lookup = new HashSet<int>(other);
 2400        return _count < lookup.Count && IsSubsetOf(lookup);
 401    }
 402
 403    /// <inheritdoc/>
 404    public bool IsProperSupersetOf(IEnumerable<int> other)
 405    {
 6406        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 407
 6408        if (other is SwiftSparseSet sparseSet)
 2409            return _count > sparseSet._count && IsSupersetOf(sparseSet);
 410
 4411        if (other is ISet<int> set)
 2412            return _count > set.Count && IsSupersetOf(set);
 413
 2414        var lookup = new HashSet<int>(other);
 2415        return _count > lookup.Count && IsSupersetOf(lookup);
 416    }
 417
 418    /// <inheritdoc/>
 419    public bool IsSubsetOf(IEnumerable<int> other)
 420    {
 10421        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 422
 12423        if (_count == 0 || ReferenceEquals(other, this)) return true;
 424
 8425        if (other is SwiftSparseSet sparseSet)
 3426            return IsSubsetOfSparseSet(sparseSet);
 427
 5428        if (other is ISet<int> set)
 4429            return IsSubsetOfSet(set);
 430
 1431        return IsSubsetOfSet(new HashSet<int>(other));
 432    }
 433
 434    /// <inheritdoc/>
 435    public bool IsSupersetOf(IEnumerable<int> other)
 436    {
 6437        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 438
 7439        if (ReferenceEquals(other, this)) return true;
 440
 29441        foreach (int item in other)
 442        {
 10443            if (!Contains(item))
 1444                return false;
 445        }
 446
 4447        return true;
 1448    }
 449
 450    /// <inheritdoc/>
 451    public bool Overlaps(IEnumerable<int> other)
 452    {
 2453        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 454
 9455        foreach (int item in other)
 456        {
 3457            if (Contains(item))
 1458                return true;
 459        }
 460
 1461        return false;
 1462    }
 463
 464    /// <inheritdoc/>
 465    public bool SetEquals(IEnumerable<int> other)
 466    {
 16467        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 468
 17469        if (ReferenceEquals(other, this)) return true;
 470
 15471        if (other is SwiftSparseSet sparseSet)
 4472            return SetEqualsSparseSet(sparseSet);
 473
 11474        if (other is ISet<int> set)
 4475            return SetEqualsSet(set);
 476
 7477        return SetEqualsSet(new HashSet<int>(other));
 478    }
 479
 480    /// <inheritdoc/>
 481    public void SymmetricExceptWith(IEnumerable<int> other)
 482    {
 5483        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 484
 5485        if (ReferenceEquals(other, this))
 486        {
 1487            Clear();
 1488            return;
 489        }
 490
 4491        if (other is SwiftSparseSet sparseSet)
 492        {
 1493            SymmetricExceptWithSet(sparseSet);
 1494            return;
 495        }
 496
 3497        if (other is ISet<int> set)
 498        {
 1499            SymmetricExceptWithSet(set);
 1500            return;
 501        }
 502
 2503        SymmetricExceptWithSet(new HashSet<int>(other));
 2504    }
 505
 506    /// <inheritdoc/>
 507    public void UnionWith(IEnumerable<int> other)
 508    {
 1509        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 510
 6511        foreach (int item in other)
 2512            Add(item);
 1513    }
 514
 515    private void RemoveWhereMissingFrom(SwiftSparseSet other)
 516    {
 1517        int index = 0;
 4518        while (index < _count)
 519        {
 3520            int key = _denseKeys[index];
 3521            if (other.Contains(key))
 2522                index++;
 523            else
 1524                Remove(key);
 525        }
 1526    }
 527
 528    private void RemoveWhereMissingFrom(ISet<int> other)
 529    {
 2530        int index = 0;
 9531        while (index < _count)
 532        {
 7533            int key = _denseKeys[index];
 7534            if (other.Contains(key))
 4535                index++;
 536            else
 3537                Remove(key);
 538        }
 2539    }
 540
 541    private bool AllKeysIn(SwiftSparseSet other)
 542    {
 28543        for (int i = 0; i < _count; i++)
 544        {
 10545            if (!other.Contains(_denseKeys[i]))
 1546                return false;
 547        }
 548
 4549        return true;
 550    }
 551
 552    private bool AllKeysIn(ISet<int> other)
 553    {
 82554        for (int i = 0; i < _count; i++)
 555        {
 30556            if (!other.Contains(_denseKeys[i]))
 3557                return false;
 558        }
 559
 11560        return true;
 561    }
 562
 563    private bool IsSubsetOfSparseSet(SwiftSparseSet other) =>
 3564        _count <= other._count && AllKeysIn(other);
 565
 566    private bool IsSubsetOfSet(ISet<int> other) =>
 5567        _count <= other.Count && AllKeysIn(other);
 568
 569    private bool SetEqualsSparseSet(SwiftSparseSet other) =>
 4570        _count == other._count && AllKeysIn(other);
 571
 572    private bool SetEqualsSet(ISet<int> other) =>
 11573        _count == other.Count && AllKeysIn(other);
 574
 575    private void SymmetricExceptWithSet(IEnumerable<int> other)
 576    {
 26577        foreach (int item in other)
 578        {
 9579            if (!Remove(item))
 4580                Add(item);
 581        }
 4582    }
 583
 584    #endregion
 585
 586    #region Capacity Management
 587
 588    /// <summary>
 589    /// Ensures that dense storage can hold at least the specified number of IDs.
 590    /// </summary>
 591    public void EnsureDenseCapacity(int capacity)
 592    {
 175593        if (capacity <= _denseKeys.Length) return;
 594
 3595        int newCap = _denseKeys.Length == 0 ? DefaultDenseCapacity : _denseKeys.Length * 2;
 5596        if (newCap < capacity) newCap = capacity;
 597
 3598        newCap = SwiftHashTools.NextPowerOfTwo(newCap);
 599
 3600        var newKeys = new int[newCap];
 3601        if (_count > 0)
 1602            Array.Copy(_denseKeys, newKeys, _count);
 603
 3604        _denseKeys = newKeys;
 3605        _version++;
 3606    }
 607
 608    /// <summary>
 609    /// Ensures that the sparse lookup table has at least the specified capacity.
 610    /// </summary>
 611    public void EnsureSparseCapacity(int capacity)
 612    {
 170613        if (capacity <= _sparse.Length) return;
 614
 8615        int newCap = _sparse.Length == 0
 8616            ? DefaultSparseCapacity
 8617            : _sparse.Length * 2;
 10618        if (newCap < capacity) newCap = capacity;
 619
 8620        newCap = SwiftHashTools.NextPowerOfTwo(newCap);
 621
 8622        var newSparse = new int[newCap];
 8623        if (_sparse.Length > 0)
 7624            Array.Copy(_sparse, newSparse, _sparse.Length);
 625
 8626        _sparse = newSparse;
 8627        _version++;
 8628    }
 629
 630    /// <summary>
 631    /// Reduces unused dense and sparse capacity while preserving all IDs.
 632    /// </summary>
 633    public void TrimExcess()
 634    {
 3635        TrimDenseStorage();
 3636        TrimSparseLookup();
 3637        _version++;
 3638    }
 639
 640    private void TrimDenseStorage()
 641    {
 3642        int newDense = Math.Max(DefaultDenseCapacity, _count);
 4643        if (newDense >= _denseKeys.Length) return;
 644
 2645        var newKeys = new int[newDense];
 2646        if (_count > 0)
 1647            Array.Copy(_denseKeys, newKeys, _count);
 2648        _denseKeys = newKeys;
 2649    }
 650
 651    private void TrimSparseLookup()
 652    {
 3653        int maxKey = -1;
 8654        for (int i = 0; i < _count; i++)
 2655            if (_denseKeys[i] > maxKey) maxKey = _denseKeys[i];
 656
 3657        int newSparse = maxKey < 0
 3658            ? DefaultSparseCapacity
 3659            : Math.Max(DefaultSparseCapacity, GetRequiredSparseCapacity(maxKey));
 4660        if (newSparse >= _sparse.Length) return;
 661
 2662        var newMap = new int[newSparse];
 6663        for (int i = 0; i < _count; i++)
 1664            newMap[_denseKeys[i]] = i + 1;
 2665        _sparse = newMap;
 2666    }
 667
 668    #endregion
 669
 670    #region Copy and Enumeration
 671
 672    /// <summary>
 673    /// Returns a read-only span over the populated dense ID range.
 674    /// </summary>
 1675    public ReadOnlySpan<int> AsReadOnlySpan() => _denseKeys.AsSpan(0, _count);
 676
 677    /// <summary>
 678    /// Retrieves the backing dense ID array and current count.
 679    /// </summary>
 680    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 681    public void GetDense(out int[] keys, out int count)
 682    {
 1683        keys = _denseKeys;
 1684        count = _count;
 1685    }
 686
 687    /// <summary>
 688    /// Replaces the destination list contents with this set's keys in dense iteration order.
 689    /// </summary>
 690    /// <remarks>
 691    /// The destination list is reused and only grows when its current capacity is smaller than
 692    /// <see cref="Count"/>. Use <see cref="CopySortedKeysTo(SwiftList{int})"/> when stable ascending
 693    /// key order is required.
 694    /// </remarks>
 695    /// <param name="destination">The caller-owned list that receives the keys.</param>
 696    public void CopyKeysTo(SwiftList<int> destination)
 697    {
 4698        SwiftThrowHelper.ThrowIfNull(destination, nameof(destination));
 699
 4700        destination.FastClear();
 4701        destination.AddRange(_denseKeys.AsSpan(0, _count));
 4702    }
 703
 704    /// <summary>
 705    /// Replaces the destination list contents with this set's keys sorted in ascending order.
 706    /// </summary>
 707    /// <remarks>
 708    /// This method is intended for reusable hot-path scratch buffers that need deterministic key order
 709    /// without constructing a persistent sorted collection.
 710    /// </remarks>
 711    /// <param name="destination">The caller-owned list that receives the sorted keys.</param>
 712    public void CopySortedKeysTo(SwiftList<int> destination)
 713    {
 3714        CopyKeysTo(destination);
 3715        destination.SortInPlace(default(SwiftIntAscendingComparer));
 3716    }
 717
 718    /// <inheritdoc/>
 719    public void CopyTo(int[] array, int arrayIndex)
 720    {
 3721        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 3722        SwiftThrowHelper.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 3723        SwiftThrowHelper.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "The array is not large enou
 724
 3725        Array.Copy(_denseKeys, 0, array, arrayIndex, _count);
 3726    }
 727
 728    /// <inheritdoc/>
 729    void ICollection.CopyTo(Array array, int index)
 730    {
 8731        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 8732        SwiftThrowHelper.ThrowIfArgument(array.Rank != 1, nameof(array), "Only single dimensional arrays are supported."
 7733        SwiftThrowHelper.ThrowIfArgument(array.GetLowerBound(0) != 0, nameof(array), "Non-zero lower bound arrays are no
 6734        SwiftThrowHelper.ThrowIfArrayIndexInvalid(index, array.Length, nameof(index));
 5735        SwiftThrowHelper.ThrowIfArgument(array.Length - index < _count, nameof(array), "The array is not large enough to
 736
 4737        if (array is int[] intArray)
 738        {
 1739            CopyTo(intArray, index);
 1740            return;
 741        }
 742
 3743        Type elementType = array.GetType().GetElementType()!;
 3744        if (array is object[] objects && elementType.IsAssignableFrom(typeof(int)))
 745        {
 6746            for (int i = 0; i < _count; i++)
 2747                objects[index + i] = _denseKeys[i];
 1748            return;
 749        }
 750
 2751        throw new ArgumentException("Invalid array type.", nameof(array));
 752    }
 753
 754    /// <inheritdoc/>
 755    public void CloneTo(ICollection<int> output)
 756    {
 1757        SwiftThrowHelper.ThrowIfNull(output, nameof(output));
 758
 1759        output.Clear();
 760
 6761        for (int i = 0; i < _count; i++)
 2762            output.Add(_denseKeys[i]);
 1763    }
 764
 765    /// <inheritdoc cref="IEnumerable.GetEnumerator()"/>
 18766    public SwiftSparseSetEnumerator GetEnumerator() => new(this);
 15767    IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
 1768    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 769
 770    /// <summary>
 771    /// Supports iteration over IDs in a <see cref="SwiftSparseSet"/>.
 772    /// </summary>
 773    public struct SwiftSparseSetEnumerator : IEnumerator<int>
 774    {
 775        private readonly SwiftSparseSet _set;
 776        private readonly int[] _keys;
 777        private readonly int _count;
 778        private readonly uint _version;
 779        private int _index;
 780
 781        internal SwiftSparseSetEnumerator(SwiftSparseSet set)
 782        {
 18783            _set = set;
 18784            _keys = set._denseKeys;
 18785            _count = set._count;
 18786            _version = set._version;
 18787            _index = -1;
 18788            Current = default;
 18789        }
 790
 791        /// <inheritdoc/>
 792        public int Current { get; private set; }
 1793        object IEnumerator.Current => Current;
 794
 795        /// <inheritdoc/>
 796        public bool MoveNext()
 797        {
 28798            SwiftThrowHelper.ThrowIfTrue(_version != _set._version, message: "Collection was modified during enumeration
 799
 27800            int next = _index + 1;
 27801            if (next >= _count)
 802            {
 15803                Current = default;
 15804                return false;
 805            }
 806
 12807            _index = next;
 12808            Current = _keys[_index];
 12809            return true;
 810        }
 811
 812        /// <inheritdoc/>
 813        public void Reset()
 814        {
 1815            SwiftThrowHelper.ThrowIfTrue(_version != _set._version, message: "Collection was modified during enumeration
 816
 1817            _index = -1;
 1818            Current = default;
 1819        }
 820
 821        /// <inheritdoc/>
 15822        public void Dispose() => _index = -1;
 823    }
 824
 825    #endregion
 826
 827    #region Helpers
 828
 829    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 830    private static int GetRequiredSparseCapacity(int key)
 831    {
 94832        SwiftThrowHelper.ThrowIfNegative(key, nameof(key));
 93833        SwiftThrowHelper.ThrowIfArgumentOutOfRange(key == int.MaxValue, key, nameof(key), "ID is too large for direct sp
 834
 92835        return key + 1;
 836    }
 837
 838    #endregion
 839}

Methods/Properties

.ctor()
.ctor(System.Int32)
.ctor(System.Int32,System.Int32)
.ctor(SwiftCollections.SwiftArrayState`1<System.Int32>)
get_Count()
get_DenseCapacity()
get_SparseCapacity()
get_IsReadOnly()
get_IsSynchronized()
get_SyncRoot()
get_DenseKeys()
get_Keys()
get_State()
set_State(SwiftCollections.SwiftArrayState`1<System.Int32>)
RestoreDenseKeys(System.Int32[])
ValidateDenseKeys(System.String)
RestoreSparseLookup(System.Int32,System.String)
Contains(System.Int32)
ContainsKey(System.Int32)
Add(System.Int32)
TryAdd(System.Int32)
System.Collections.Generic.ICollection<System.Int32>.Add(System.Int32)
Remove(System.Int32)
Clear()
ExceptWith(System.Collections.Generic.IEnumerable`1<System.Int32>)
IntersectWith(System.Collections.Generic.IEnumerable`1<System.Int32>)
IsProperSubsetOf(System.Collections.Generic.IEnumerable`1<System.Int32>)
IsProperSupersetOf(System.Collections.Generic.IEnumerable`1<System.Int32>)
IsSubsetOf(System.Collections.Generic.IEnumerable`1<System.Int32>)
IsSupersetOf(System.Collections.Generic.IEnumerable`1<System.Int32>)
Overlaps(System.Collections.Generic.IEnumerable`1<System.Int32>)
SetEquals(System.Collections.Generic.IEnumerable`1<System.Int32>)
SymmetricExceptWith(System.Collections.Generic.IEnumerable`1<System.Int32>)
UnionWith(System.Collections.Generic.IEnumerable`1<System.Int32>)
RemoveWhereMissingFrom(SwiftCollections.SwiftSparseSet)
RemoveWhereMissingFrom(System.Collections.Generic.ISet`1<System.Int32>)
AllKeysIn(SwiftCollections.SwiftSparseSet)
AllKeysIn(System.Collections.Generic.ISet`1<System.Int32>)
IsSubsetOfSparseSet(SwiftCollections.SwiftSparseSet)
IsSubsetOfSet(System.Collections.Generic.ISet`1<System.Int32>)
SetEqualsSparseSet(SwiftCollections.SwiftSparseSet)
SetEqualsSet(System.Collections.Generic.ISet`1<System.Int32>)
SymmetricExceptWithSet(System.Collections.Generic.IEnumerable`1<System.Int32>)
EnsureDenseCapacity(System.Int32)
EnsureSparseCapacity(System.Int32)
TrimExcess()
TrimDenseStorage()
TrimSparseLookup()
AsReadOnlySpan()
GetDense(System.Int32[]&,System.Int32&)
CopyKeysTo(SwiftCollections.SwiftList`1<System.Int32>)
CopySortedKeysTo(SwiftCollections.SwiftList`1<System.Int32>)
CopyTo(System.Int32[],System.Int32)
System.Collections.ICollection.CopyTo(System.Array,System.Int32)
CloneTo(System.Collections.Generic.ICollection`1<System.Int32>)
GetEnumerator()
System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator()
System.Collections.IEnumerable.GetEnumerator()
.ctor(SwiftCollections.SwiftSparseSet)
System.Collections.IEnumerator.get_Current()
MoveNext()
Reset()
Dispose()
GetRequiredSparseCapacity(System.Int32)