< Summary

Line coverage
98%
Covered lines: 193
Uncovered lines: 2
Coverable lines: 195
Total lines: 613
Line coverage: 98.9%
Branch coverage
98%
Covered branches: 85
Total branches: 86
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
File 1: .cctor()100%11100%
File 2: .cctor()100%11100%
File 2: .ctor()100%11100%
File 2: .ctor(...)100%22100%
File 2: .ctor(...)100%11100%
File 2: get_Count()100%11100%
File 2: get_Capacity()100%11100%
File 2: get_IsSynchronized()100%11100%
File 2: get_SyncRoot()100%22100%
File 2: get_Dense()100%11100%
File 2: get_IsReadOnly()100%11100%
File 2: get_State()100%11100%
File 2: set_State(...)100%66100%
File 2: Contains(...)100%11100%
File 2: AsReadOnlySpan()100%11100%
File 2: Exists(...)100%44100%
File 2: Find(...)100%44100%
File 2: Add(...)100%22100%
File 2: System.Collections.Generic.ICollection<T>.Add(...)100%210%
File 2: Remove(...)100%66100%
File 2: Clear()100%44100%
File 2: EnsureCapacity(...)100%22100%
File 2: GetEnumerator()100%11100%
File 2: System.Collections.Generic.IEnumerable<T>.GetEnumerator()100%11100%
File 2: 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%22100%
File 2: Reset()100%11100%
File 2: Dispose()100%11100%
File 2: CloneTo(...)100%22100%
File 2: CopyTo(...)100%11100%
File 2: ExceptWith(...)100%44100%
File 2: IntersectWith(...)100%66100%
File 2: IsProperSubsetOf(...)83.33%6687.5%
File 2: IsProperSupersetOf(...)100%66100%
File 2: IsSubsetOf(...)100%66100%
File 2: IsSupersetOf(...)100%44100%
File 2: Overlaps(...)100%44100%
File 2: SetEquals(...)100%66100%
File 2: SymmetricExceptWith(...)100%66100%
File 2: UnionWith(...)100%22100%

File(s)

/_/src/SwiftCollections/obj/Release/net8.0/MemoryPack.Generator/MemoryPack.Generator.MemoryPackGenerator/SwiftCollections.SwiftPackedSet_T_.MemoryPackFormatter.g.cs

File '/_/src/SwiftCollections/obj/Release/net8.0/MemoryPack.Generator/MemoryPack.Generator.MemoryPackGenerator/SwiftCollections.SwiftPackedSet_T_.MemoryPackFormatter.g.cs' does not exist (any more).

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftPackedSet.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 set that stores unique values in a densely packed array
 13/// while providing O(1) lookups via an internal hash map.
 14/// </summary>
 15/// <remarks>
 16/// <para>
 17/// <see cref="SwiftPackedSet{T}"/> maintains values in a contiguous array for extremely
 18/// cache-friendly iteration while using a hash-based lookup table to guarantee fast
 19/// membership tests and removals.
 20/// </para>
 21/// <para>
 22/// Removal uses a swap-back strategy that keeps the dense storage contiguous but does not
 23/// preserve ordering. As a result, iteration order is not guaranteed to remain stable.
 24/// </para>
 25/// <para>
 26/// This structure is commonly used in high-performance systems such as ECS (Entity Component Systems)
 27/// where dense iteration speed is critical.
 28/// </para>
 29/// </remarks>
 30/// <typeparam name="T">The type of elements contained in the set.</typeparam>
 31[Serializable]
 32[JsonConverter(typeof(StateJsonConverterFactory))]
 33[MemoryPackable]
 34public sealed partial class SwiftPackedSet<T> : IStateBacked<SwiftArrayState<T>>, ISwiftCloneable<T>, ISet<T>, IEnumerab
 35    where T : notnull
 36{
 37    #region Constants
 38
 39    /// <summary>
 40    /// Represents the default initial capacity value used when no specific capacity is provided.
 41    /// </summary>
 42    public const int DefaultCapacity = 8;
 43
 244    private static readonly bool _clearReleasedSlots = RuntimeHelpers.IsReferenceOrContainsReferences<T>();
 45
 46    #endregion
 47
 48    #region Fields
 49
 50    private T[] _dense;
 51    private SwiftDictionary<T, int> _lookup;
 52    private int _count;
 53
 54    [NonSerialized]
 55    private uint _version;
 56
 57    [NonSerialized]
 58    private object? _syncRoot;
 59
 60    #endregion
 61
 62    #region Constructors
 63
 64    /// <summary>
 65    /// Initializes a new instance of the SwiftPackedSet class with the default capacity.
 66    /// </summary>
 9267    public SwiftPackedSet() : this(DefaultCapacity) { }
 68
 69    /// <summary>
 70    /// Initializes a new instance of the SwiftPackedSet class with the specified initial capacity.
 71    /// </summary>
 72    /// <remarks>
 73    /// The actual capacity is rounded up to the next power of two greater than or equal to the specified value,
 74    /// unless the specified value is less than or equal to the default capacity.
 75    /// </remarks>
 76    /// <param name="capacity">
 77    /// The initial number of elements that the set can contain before resizing.
 78    /// If less than or equal to the default capacity, the default capacity is used.
 79    /// Must be non-negative.
 80    /// </param>
 4781    public SwiftPackedSet(int capacity)
 82    {
 4783        capacity = capacity <= DefaultCapacity
 4784            ? DefaultCapacity
 4785            : SwiftHashTools.NextPowerOfTwo(capacity);
 86
 4787        _dense = new T[capacity];
 4788        _lookup = new SwiftDictionary<T, int>(capacity);
 4789    }
 90
 91    /// <summary>
 92    /// Initializes a new instance of the SwiftPackedSet class with the specified array state.
 93    /// </summary>
 94    /// <param name="state">The state object that provides the initial data and configuration for the set. Cannot be nul
 95    [MemoryPackConstructor]
 796    public SwiftPackedSet(SwiftArrayState<T> state)
 97    {
 798        State = state;
 99
 7100        SwiftThrowHelper.ThrowIfNull(_dense, nameof(_dense));
 7101        SwiftThrowHelper.ThrowIfNull(_lookup, nameof(_lookup));
 7102    }
 103
 104    #endregion
 105
 106    #region Properties
 107
 108    /// <summary>
 109    /// Gets the number of elements contained in the collection.
 110    /// </summary>
 111    [JsonIgnore]
 112    [MemoryPackIgnore]
 19113    public int Count => _count;
 114
 115    /// <summary>
 116    /// Gets the total number of elements that the collection can hold without resizing.
 117    /// </summary>
 118    [JsonIgnore]
 119    [MemoryPackIgnore]
 4120    public int Capacity => _dense.Length;
 121
 122    /// <summary>
 123    /// Gets a value indicating whether access to the collection is synchronized (thread safe).
 124    /// </summary>
 125    [JsonIgnore]
 126    [MemoryPackIgnore]
 1127    public bool IsSynchronized => false;
 128
 129    /// <inheritdoc/>
 130    [JsonIgnore]
 131    [MemoryPackIgnore]
 1132    public object SyncRoot => _syncRoot ??= new object();
 133
 134    /// <summary>
 135    /// Gets the underlying dense array of elements.
 136    /// </summary>
 137    [JsonIgnore]
 138    [MemoryPackIgnore]
 8139    public T[] Dense => _dense;
 140
 141    /// <inheritdoc/>
 142    [JsonIgnore]
 143    [MemoryPackIgnore]
 1144    public bool IsReadOnly => false;
 145
 146    /// <summary>
 147    /// Gets or sets the current state of the array, including its items and order.
 148    /// </summary>
 149    /// <remarks>
 150    /// Use this property to capture or restore the array's contents and structure.
 151    /// Setting this property replaces the entire array with the provided state.
 152    /// </remarks>
 153    [JsonInclude]
 154    [MemoryPackInclude]
 155    public SwiftArrayState<T> State
 156    {
 157        get
 158        {
 6159            var values = new T[_count];
 6160            Array.Copy(_dense, values, _count);
 161
 6162            return new SwiftArrayState<T>(values);
 163        }
 164        internal set
 165        {
 7166            SwiftThrowHelper.ThrowIfNull(value.Items, nameof(value.Items));
 167
 7168            var values = value.Items;
 169
 7170            int n = values.Length;
 7171            int newCapacity = n < DefaultCapacity
 7172                ? DefaultCapacity
 7173                : SwiftHashTools.NextPowerOfTwo(n);
 174
 7175            _dense = new T[newCapacity];
 7176            _lookup = new SwiftDictionary<T, int>(newCapacity);
 177
 7178            if (n > 0)
 179            {
 4180                Array.Copy(values, _dense, n);
 181
 2022182                for (int i = 0; i < n; i++)
 1007183                    _lookup.Add(values[i], i);
 184            }
 185
 7186            _count = n;
 7187            _version++;
 7188        }
 189    }
 190
 191    #endregion
 192
 193    #region Core Operations
 194
 195    /// <inheritdoc/>
 196    public bool Contains(T value)
 1035197        => _lookup.ContainsKey(value);
 198
 199    /// <summary>
 200    /// Returns a read-only span over the populated dense portion of the set.
 201    /// </summary>
 1202    public ReadOnlySpan<T> AsReadOnlySpan() => _dense.AsSpan(0, _count);
 203
 204    /// <summary>
 205    /// Determines whether the <see cref="SwiftPackedSet{T}"/> contains an element that matches the conditions defined b
 206    /// </summary>
 207    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 208    /// <returns><c>true</c> if the <see cref="SwiftPackedSet{T}"/> contains one or more elements that match the specifi
 209    public bool Exists(Predicate<T> match)
 210    {
 3211        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 212
 10213        for (int i = 0; i < _count; i++)
 214        {
 4215            if (match(_dense[i]))
 1216                return true;
 217        }
 218
 1219        return false;
 220    }
 221
 222    /// <summary>
 223    /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first ma
 224    /// </summary>
 225    /// <param name="match">The predicate that defines the conditions of the element to search for.</param>
 226    /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, 
 227    public T Find(Predicate<T> match)
 228    {
 2229        SwiftThrowHelper.ThrowIfNull(match, nameof(match));
 230
 10231        for (int i = 0; i < _count; i++)
 232        {
 4233            if (match(_dense[i]))
 1234                return _dense[i];
 235        }
 236
 1237        return default!;
 238    }
 239
 240    /// <inheritdoc/>
 241    public bool Add(T value)
 242    {
 11096243        if (_lookup.ContainsKey(value))
 1244            return false;
 245
 11095246        EnsureCapacity(_count + 1);
 247
 11095248        _dense[_count] = value;
 11095249        _lookup.Add(value, _count);
 250
 11095251        _count++;
 11095252        _version++;
 253
 11095254        return true;
 255    }
 256
 0257    void ICollection<T>.Add(T item) => Add(item);
 258
 259    /// <inheritdoc/>
 260    public bool Remove(T value)
 261    {
 5013262        if (!_lookup.TryGetValue(value, out int index))
 3263            return false;
 264
 5010265        int last = --_count;
 266
 5010267        if (index != last)
 268        {
 5006269            T moved = _dense[last];
 270
 5006271            _dense[index] = moved;
 5006272            _lookup[moved] = index;
 273        }
 274
 5010275        if (_clearReleasedSlots)
 3276            _dense[last] = default!;
 5010277        _lookup.Remove(value);
 278
 5010279        _version++;
 5010280        return true;
 281    }
 282
 283    /// <inheritdoc/>
 284    public void Clear()
 285    {
 8286        if (_count == 0) return;
 287
 6288        if (_clearReleasedSlots)
 1289            Array.Clear(_dense, 0, _count);
 6290        _lookup.Clear();
 291
 6292        _count = 0;
 6293        _version++;
 6294    }
 295
 296    #endregion
 297
 298    #region Capacity
 299
 300    /// <summary>
 301    /// Ensures that the internal storage has at least the specified capacity, expanding it if necessary.
 302    /// </summary>
 303    /// <remarks>
 304    /// If the current capacity is less than the specified value, the internal storage is resized to
 305    /// the next power of two greater than or equal to the specified capacity.
 306    /// Existing elements are preserved.
 307    /// </remarks>
 308    /// <param name="capacity">The minimum number of elements that the internal storage should be able to hold. Must be 
 309    public void EnsureCapacity(int capacity)
 310    {
 11096311        int newCapacity = SwiftHashTools.NextPowerOfTwo(capacity);
 11096312        if (newCapacity <= _dense.Length)
 11078313            return;
 314
 18315        var newArray = new T[newCapacity];
 18316        Array.Copy(_dense, newArray, _count);
 317
 18318        _dense = newArray;
 18319    }
 320
 321    #endregion
 322
 323    #region Enumeration
 324
 325    /// <inheritdoc cref="IEnumerable.GetEnumerator()"/>
 15326    public SwiftPackedSetEnumerator GetEnumerator() => new(this);
 12327    IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
 1328    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 329
 330    /// <summary>
 331    /// Enumerates the elements of a <see cref="SwiftPackedSet{T}"/> collection.
 332    /// </summary>
 333    /// <remarks>
 334    /// The enumerator is invalidated if the collection is modified after the enumerator is created.
 335    /// In such cases, calling MoveNext or Reset will throw an InvalidOperationException.
 336    /// </remarks>
 337    public struct SwiftPackedSetEnumerator : IEnumerator<T>
 338    {
 339        private readonly SwiftPackedSet<T> _set;
 340        private readonly uint _version;
 341        private int _index;
 342
 343        internal SwiftPackedSetEnumerator(SwiftPackedSet<T> set)
 344        {
 15345            _set = set;
 15346            _version = set._version;
 15347            _index = -1;
 15348            Current = default!;
 15349        }
 350
 351        /// <inheritdoc/>
 44352        public T Current { get; private set; }
 353
 2354        object IEnumerator.Current => Current;
 355
 356        /// <inheritdoc/>
 357        public bool MoveNext()
 358        {
 19359            SwiftThrowHelper.ThrowIfTrue(_version != _set._version, message: "Collection was modified during enumeration
 360
 18361            int next = _index + 1;
 18362            if (next >= _set._count)
 363            {
 12364                Current = default!;
 12365                return false;
 366            }
 367
 6368            _index = next;
 6369            Current = _set._dense[next];
 6370            return true;
 371        }
 372
 373        /// <inheritdoc/>
 374        public void Reset()
 375        {
 2376            SwiftThrowHelper.ThrowIfTrue(_version != _set._version, message: "Collection was modified during enumeration
 377
 1378            _index = -1;
 1379            Current = default!;
 1380        }
 381
 382        /// <inheritdoc/>
 12383        public void Dispose() => _index = -1;
 384    }
 385
 386    #endregion
 387
 388    #region Clone
 389
 390    /// <inheritdoc/>
 391    public void CloneTo(ICollection<T> output)
 392    {
 1393        SwiftThrowHelper.ThrowIfNull(output, nameof(output));
 394
 1395        output.Clear();
 396
 6397        for (int i = 0; i < _count; i++)
 2398            output.Add(_dense[i]);
 1399    }
 400
 401    /// <inheritdoc/>
 402    public void CopyTo(T[] array, int arrayIndex)
 403    {
 3404        SwiftThrowHelper.ThrowIfNull(array, nameof(array));
 3405        SwiftThrowHelper.ThrowIfArrayIndexInvalid(arrayIndex, array.Length, message: "Array index is out of range.");
 2406        SwiftThrowHelper.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "Destination array is not lo
 407
 1408        Array.Copy(_dense, 0, array, arrayIndex, _count);
 1409    }
 410
 411    #endregion
 412
 413    #region ISet<T> Implementations
 414
 415    /// <inheritdoc/>
 416    public void ExceptWith(IEnumerable<T> other)
 417    {
 2418        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 419
 2420        if (ReferenceEquals(this, other))
 421        {
 1422            Clear();
 1423            return;
 424        }
 425
 1426        var otherSet = new SwiftHashSet<T>(other);
 427
 8428        foreach (var item in otherSet)
 3429            Remove(item);
 1430    }
 431
 432    /// <inheritdoc/>
 433    public void IntersectWith(IEnumerable<T> other)
 434    {
 2435        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 436
 2437        if (ReferenceEquals(this, other))
 1438            return;
 439
 1440        var otherSet = new SwiftHashSet<T>(other);
 441
 10442        for (int i = _count - 1; i >= 0; i--)
 443        {
 4444            var value = _dense[i];
 4445            if (!otherSet.Contains(value))
 2446                Remove(value);
 447        }
 1448    }
 449
 450    /// <inheritdoc/>
 451    public bool IsProperSubsetOf(IEnumerable<T> other)
 452    {
 2453        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 454
 2455        var set = new SwiftHashSet<T>(other);
 456
 2457        if (_count >= set.Count)
 1458            return false;
 459
 8460        for (int i = 0; i < _count; i++)
 3461            if (!set.Contains(_dense[i]))
 0462                return false;
 463
 1464        return true;
 465    }
 466
 467    /// <inheritdoc/>
 468    public bool IsProperSupersetOf(IEnumerable<T> other)
 469    {
 3470        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 471
 3472        var set = new SwiftHashSet<T>(other);
 473
 3474        if (_count <= set.Count)
 1475            return false;
 476
 9477        foreach (var item in set)
 3478            if (!Contains(item))
 1479                return false;
 480
 1481        return true;
 1482    }
 483
 484    /// <inheritdoc/>
 485    public bool IsSubsetOf(IEnumerable<T> other)
 486    {
 3487        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 488
 3489        var set = new SwiftHashSet<T>(other);
 490
 3491        if (_count > set.Count)
 1492            return false;
 493
 14494        for (int i = 0; i < _count; i++)
 6495            if (!set.Contains(_dense[i]))
 1496                return false;
 497
 1498        return true;
 499    }
 500
 501    /// <inheritdoc/>
 502    public bool IsSupersetOf(IEnumerable<T> other)
 503    {
 2504        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 505
 13506        foreach (var item in other)
 5507            if (!Contains(item))
 1508                return false;
 509
 1510        return true;
 1511    }
 512
 513    /// <inheritdoc/>
 514    public bool Overlaps(IEnumerable<T> other)
 515    {
 2516        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 517
 9518        foreach (var item in other)
 3519            if (Contains(item))
 1520                return true;
 521
 1522        return false;
 1523    }
 524
 525    /// <inheritdoc/>
 526    public bool SetEquals(IEnumerable<T> other)
 527    {
 6528        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 529
 6530        var set = new SwiftHashSet<T>(other);
 531
 6532        if (_count != set.Count)
 1533            return false;
 534
 38535        for (int i = 0; i < _count; i++)
 15536            if (!set.Contains(_dense[i]))
 1537                return false;
 538
 4539        return true;
 540    }
 541
 542    /// <inheritdoc/>
 543    public void SymmetricExceptWith(IEnumerable<T> other)
 544    {
 2545        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 546
 2547        if (ReferenceEquals(this, other))
 548        {
 1549            Clear();
 1550            return;
 551        }
 552
 1553        var set = new SwiftHashSet<T>(other);
 554
 6555        foreach (var item in set)
 556        {
 2557            if (!Remove(item))
 1558                Add(item);
 559        }
 1560    }
 561
 562    /// <inheritdoc/>
 563    public void UnionWith(IEnumerable<T> other)
 564    {
 1565        SwiftThrowHelper.ThrowIfNull(other, nameof(other));
 566
 6567        foreach (var item in other)
 2568            Add(item);
 1569    }
 570
 571    #endregion
 572}