< Summary

Line coverage
100%
Covered lines: 492
Uncovered lines: 0
Coverable lines: 492
Total lines: 1496
Line coverage: 100%
Branch coverage
100%
Covered branches: 222
Total branches: 222
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: .cctor()100%44100%
File 1: .ctor()100%11100%
File 1: .ctor(...)100%11100%
File 1: .ctor(...)100%22100%
File 1: .ctor(...)100%44100%
File 1: .ctor(...)100%11100%
File 1: get_Count()100%11100%
File 1: get_Capacity()100%11100%
File 1: get_Comparer()100%11100%
File 1: get_Item(...)100%22100%
File 1: set_Item(...)100%22100%
File 1: System.Collections.IDictionary.get_Item(...)100%44100%
File 1: System.Collections.IDictionary.set_Item(...)100%11100%
File 1: get_Keys()100%22100%
File 1: System.Collections.IDictionary.get_Keys()100%22100%
File 1: get_Values()100%22100%
File 1: System.Collections.IDictionary.get_Values()100%22100%
File 1: System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.get_IsReadOnly()100%11100%
File 1: System.Collections.IDictionary.get_IsReadOnly()100%11100%
File 1: System.Collections.IDictionary.get_IsFixedSize()100%11100%
File 1: System.Collections.ICollection.get_IsSynchronized()100%11100%
File 1: get_SyncRoot()100%22100%
File 1: get_State()100%22100%
File 1: set_State(...)100%44100%
File 1: Add(...)100%11100%
File 1: Add(...)100%11100%
File 1: System.Collections.Generic.IDictionary<TKey,TValue>.Add(...)100%11100%
File 1: Add(...)100%11100%
File 1: InsertIfNotExist(...)100%2626100%
File 1: Remove(...)100%1818100%
File 1: System.Collections.IDictionary.Remove(...)100%22100%
File 1: Remove(...)100%44100%
File 1: Clear()100%44100%
File 1: Clear(...)100%66100%
File 1: CheckLoadThreshold()100%22100%
File 1: EnsureCapacity(...)100%22100%
File 1: Resize(...)100%88100%
File 1: TrimExcess()100%1212100%
File 1: CalculateAdaptiveResizeFactors(...)100%66100%
File 1: Initialize(...)100%22100%
File 1: ContainsKey(...)100%11100%
File 1: System.Collections.IDictionary.Contains(...)100%22100%
File 1: Contains(...)100%44100%
File 1: TryGetValue(...)100%22100%
File 1: CopyTo(...)100%44100%
File 1: CopyTo(...)100%44100%
File 1: CopyToDictionaryEntries(...)100%44100%
File 1: CopyToObjects(...)100%22100%
File 1: CopyToObjects(...)100%44100%
File 1: SetComparer(...)100%22100%
File 1: SwitchToRandomizedComparer()100%11100%
File 1: RehashEntries()100%88100%
File 1: FindEntry(...)100%1616100%
File 1: GetEnumerator()100%11100%
File 1: System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>.GetEnumerator()100%11100%
File 1: System.Collections.IEnumerable.GetEnumerator()100%11100%
File 1: System.Collections.IDictionary.GetEnumerator()100%11100%
File 2: .ctor(...)100%11100%
File 2: get_Count()100%11100%
File 2: System.Collections.ICollection.get_IsSynchronized()100%11100%
File 2: System.Collections.ICollection.get_SyncRoot()100%11100%
File 2: System.Collections.Generic.ICollection<TKey>.get_IsReadOnly()100%11100%
File 2: System.Collections.Generic.ICollection<TKey>.Add(...)100%11100%
File 2: System.Collections.Generic.ICollection<TKey>.Clear()100%11100%
File 2: System.Collections.Generic.ICollection<TKey>.Contains(...)100%11100%
File 2: System.Collections.Generic.ICollection<TKey>.Remove(...)100%11100%
File 2: CopyTo(...)100%44100%
File 2: System.Collections.ICollection.CopyTo(...)100%88100%
File 2: GetEnumerator()100%11100%
File 2: System.Collections.Generic.IEnumerable<TKey>.GetEnumerator()100%11100%
File 2: System.Collections.IEnumerable.GetEnumerator()100%11100%
File 3: .ctor(...)100%11100%
File 3: get_Current()100%11100%
File 3: System.Collections.IEnumerator.get_Current()100%11100%
File 3: MoveNext()100%44100%
File 3: Reset()100%11100%
File 3: Dispose()100%11100%
File 4: .ctor(...)100%11100%
File 4: System.Collections.IDictionaryEnumerator.get_Key()100%11100%
File 4: System.Collections.IDictionaryEnumerator.get_Value()100%11100%
File 4: System.Collections.IDictionaryEnumerator.get_Entry()100%11100%
File 4: get_Current()100%11100%
File 4: System.Collections.IEnumerator.get_Current()100%22100%
File 4: MoveNext()100%44100%
File 4: Reset()100%11100%
File 4: Dispose()100%11100%
File 5: .ctor(...)100%11100%
File 5: get_Count()100%11100%
File 5: System.Collections.Generic.ICollection<TValue>.get_IsReadOnly()100%11100%
File 5: System.Collections.ICollection.get_IsSynchronized()100%11100%
File 5: System.Collections.ICollection.get_SyncRoot()100%11100%
File 5: System.Collections.Generic.ICollection<TValue>.Add(...)100%11100%
File 5: System.Collections.Generic.ICollection<TValue>.Clear()100%11100%
File 5: System.Collections.Generic.ICollection<TValue>.Contains(...)100%66100%
File 5: System.Collections.Generic.ICollection<TValue>.Remove(...)100%11100%
File 5: CopyTo(...)100%44100%
File 5: System.Collections.ICollection.CopyTo(...)100%88100%
File 5: GetEnumerator()100%11100%
File 5: System.Collections.Generic.IEnumerable<TValue>.GetEnumerator()100%11100%
File 5: System.Collections.IEnumerable.GetEnumerator()100%11100%
File 6: .ctor(...)100%11100%
File 6: get_Current()100%11100%
File 6: System.Collections.IEnumerator.get_Current()100%11100%
File 6: MoveNext()100%44100%
File 6: Reset()100%11100%
File 6: Dispose()100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// SwiftDictionary.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
 22
 23/// <summary>
 24/// A high-performance, memory-efficient dictionary providing lightning-fast O(1) operations for addition, retrieval, an
 25/// </summary>
 26/// <typeparam name="TKey">Specifies the type of keys in the dictionary.</typeparam>
 27/// <typeparam name="TValue">Specifies the type of values in the dictionary.</typeparam>
 28/// <remarks>
 29/// The comparer is not serialized. After deserialization the dictionary reverts
 30/// to the same default comparer selection used by a new instance. String keys
 31/// use SwiftCollections' deterministic default comparer. Object keys use a
 32/// SwiftCollections comparer that hashes strings deterministically, while other
 33/// object-key determinism still depends on the underlying key type's
 34/// <see cref="object.GetHashCode()"/> implementation. Other key types use
 35/// <see cref="EqualityComparer{TKey}.Default"/>.
 36///
 37/// If a custom comparer is required it can be reapplied using
 38/// <see cref="SetComparer(IEqualityComparer{TKey})"/>.
 39/// </remarks>
 40[Serializable]
 41[JsonConverter(typeof(StateJsonConverterFactory))]
 42[MemoryPackable]
 43public partial class SwiftDictionary<TKey, TValue> : IStateBacked<SwiftDictionaryState<TKey, TValue>>, IDictionary<TKey,
 44    where TKey : notnull
 45{
 46    #region Constants
 47
 48    /// <summary>
 49    /// The default initial capacity of the dictionary.
 50    /// </summary>
 51    public const int DefaultCapacity = 8;
 52
 53    /// <summary>
 54    /// Determines the maximum allowable load factor before resizing the hash set to maintain performance.
 55    /// </summary>
 56    private const double _LoadFactorThreshold = 0.82;
 57
 58    #endregion
 59
 60    #region Fields
 61
 62    // Skip per-operation boxing for non-nullable value keys, including in Debug builds.
 63    // A cold lookup must not box a default value while initializing this flag.
 1564    private static readonly bool _KeyCanBeNull = !typeof(TKey).IsValueType
 1565        || (typeof(TKey).IsGenericType && typeof(TKey).GetGenericTypeDefinition() == typeof(Nullable<>));
 66
 67    /// <summary>
 68    /// The array containing the entries of the dictionary.
 69    /// </summary>
 70    protected internal Entry[] _entries;
 71
 72    /// <summary>
 73    /// The total number of entries in the dictionary
 74    /// </summary>
 75    private int _count;
 76
 77    /// <summary>
 78    /// The index of the last used entry in the dictionary.
 79    /// </summary>
 80    private int _lastIndex;
 81
 82    /// <summary>
 83    /// A mask used for efficiently computing the entry arrayIndex from a hash code.
 84    /// This is typically the size of the entry array minus one, assuming the size is a power of two.
 85    /// </summary>
 86    private int _entryMask;
 87
 88    /// <summary>
 89    /// The comparer used to determine equality of keys and to generate hash codes.
 90    /// </summary>
 91    protected IEqualityComparer<TKey> _comparer;
 92
 93    /// <summary>
 94    /// Specifies the dynamic growth factor for resizing, adjusted based on recent usage patterns.
 95    /// </summary>
 96    private int _adaptiveResizeFactor;
 97
 98    /// <summary>
 99    /// Tracks the count threshold at which the hash set should resize based on the load factor.
 100    /// </summary>
 101    private uint _nextResizeCount;
 102
 103    /// <summary>
 104    /// Represents the moving average of the fill rate, used to dynamically adjust resizing behavior.
 105    /// </summary>
 106    private double _movingFillRate;
 107
 108    /// <summary>
 109    /// The maximum number of steps allowed during probing to resolve collisions.
 110    /// </summary>
 111    private int _maxStepCount;
 112
 113    /// <summary>
 114    /// A version counter used to track modifications to the dictionary.
 115    /// Incremented on mutations to detect changes during enumeration and ensure enumerator validity.
 116    /// </summary>
 117    [NonSerialized]
 118    protected uint _version;
 119
 120    /// <summary>
 121    /// An object that can be used to synchronize access to the SwiftDictionary.
 122    /// </summary>
 123    [NonSerialized]
 124    private object? _syncRoot;
 125
 126    #endregion
 127
 128    #region Constructors
 129
 130    /// <summary>
 131    /// Initialize a new instance of <see cref="SwiftDictionary{TKey, TValue}"/> with customizable capacity and comparer
 132    /// </summary>
 354133    public SwiftDictionary() : this(DefaultCapacity, null) { }
 134
 135    /// <inheritdoc cref="SwiftDictionary()"/>
 427136    public SwiftDictionary(int capacity, IEqualityComparer<TKey>? comparer = null)
 137    {
 427138        Initialize(capacity, comparer);
 139
 427140        ChronicleGuard.ThrowIfNull(_entries, nameof(_entries));
 427141        ChronicleGuard.ThrowIfNull(_comparer, nameof(_comparer));
 427142    }
 143
 144    /// <inheritdoc cref="SwiftDictionary()"/>
 2145    public SwiftDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey>? comparer = null)
 146    {
 2147        ChronicleGuard.ThrowIfNull(dictionary, nameof(dictionary));
 148
 1149        Initialize(dictionary.Count, comparer);
 150
 1151        ChronicleGuard.ThrowIfNull(_entries, nameof(_entries));
 1152        ChronicleGuard.ThrowIfNull(_comparer, nameof(_comparer));
 153
 6154        foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
 2155            InsertIfNotExist(kvp.Key, kvp.Value);
 1156    }
 157
 158    /// <inheritdoc cref="SwiftDictionary()"/>
 3159    public SwiftDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey>? comparer = null)
 160    {
 3161        ChronicleGuard.ThrowIfNull(collection, nameof(collection));
 162
 2163        int count = (collection as ICollection<KeyValuePair<TKey, TValue>>)?.Count ?? DefaultCapacity;
 164        // Dynamic padding based on collision estimation
 2165        int size = (int)(count / _LoadFactorThreshold);
 2166        Initialize(size, comparer);
 167
 2168        ChronicleGuard.ThrowIfNull(_entries, nameof(_entries));
 2169        ChronicleGuard.ThrowIfNull(_comparer, nameof(_comparer));
 170
 74171        foreach (KeyValuePair<TKey, TValue> kvp in collection)
 35172            InsertIfNotExist(kvp.Key, kvp.Value);
 2173    }
 174
 175    ///  <summary>
 176    ///  Initializes a new instance of the <see cref="SwiftDictionary{TKey, TValue}"/> class with the specified <see cre
 177    ///  </summary>
 178    ///  <param name="state">The state containing the internal array, count, offset, and version for initialization.</pa
 179    [MemoryPackConstructor]
 11180    public SwiftDictionary(SwiftDictionaryState<TKey, TValue> state)
 181    {
 11182        State = state;
 183
 11184        ChronicleGuard.ThrowIfNull(_entries, nameof(_entries));
 11185        ChronicleGuard.ThrowIfNull(_comparer, nameof(_comparer));
 11186    }
 187
 188    #endregion
 189
 190    #region Properties
 191
 192    /// <summary>
 193    /// Gets the number of elements contained in the dictionary.
 194    /// </summary>
 195    [JsonIgnore]
 196    [MemoryPackIgnore]
 34197    public int Count => _count;
 198
 199    /// <summary>
 200    /// Gets the total number of elements that the collection can hold without resizing.
 201    /// </summary>
 202    [JsonIgnore]
 203    [MemoryPackIgnore]
 17204    public int Capacity => _entries.Length;
 205
 206    /// <summary>
 207    /// Gets the equality comparer used to determine equality of keys in the collection.
 208    /// </summary>
 209    [JsonIgnore]
 210    [MemoryPackIgnore]
 12211    public IEqualityComparer<TKey> Comparer => _comparer;
 212
 213    /// <summary>
 214    /// Gets or sets the value associated with the specified key.
 215    /// </summary>
 216    /// <remarks>
 217    /// Getting a value with a key that does not exist will throw an exception.
 218    /// Setting a value for a key that does not exist will add a new entry with the specified key and value.
 219    /// </remarks>
 220    /// <param name="key">The key whose value to get or set.</param>
 221    [JsonIgnore]
 222    [MemoryPackIgnore]
 223    public TValue this[TKey key]
 224    {
 225        get
 226        {
 5479227            int index = FindEntry(key);
 228            // The object-based error helper would box a value key even on a successful lookup.
 5479229            if (index < 0)
 4230                ChronicleGuard.ThrowIfKeyInvalid(index, key);
 5475231            return _entries[index].Value;
 232        }
 233        set
 234        {
 9913235            int index = FindEntry(key);
 9913236            if (index >= 0)
 6512237                _entries[index].Value = value;
 238            else
 239            {
 3401240                CheckLoadThreshold();
 3401241                InsertIfNotExist(key, value);
 242            }
 3399243        }
 244    }
 245
 246    /// <inheritdoc/>
 247    [JsonIgnore]
 248    [MemoryPackIgnore]
 249    object? IDictionary.this[object obj]
 250    {
 251        get
 252        {
 19253            ChronicleGuard.ThrowIfNull(obj, nameof(obj));
 254
 18255            if (obj is TKey key)
 256            {
 17257                int index = FindEntry(key);
 33258                if (index >= 0) return _entries[index].Value;
 259            }
 2260            return null;
 261        }
 262        set
 263        {
 5264            ChronicleGuard.ThrowIfNullAndNullsAreIllegal(value, default(TValue));
 265            try
 266            {
 5267                TKey tempKey = (TKey)obj;
 268                try
 269                {
 3270                    this[tempKey] = (TValue)value!;
 1271                }
 2272                catch (InvalidCastException)
 273                {
 2274                    throw new ArgumentException($"Value {value} does not match expected {typeof(TValue)}");
 275                }
 1276            }
 2277            catch (InvalidCastException)
 278            {
 2279                throw new ArgumentException($"Key {obj} does not match expected {typeof(TKey)}");
 280            }
 1281        }
 282    }
 283
 284    /// <summary>
 285    /// The collection containing the keys of the dictionary.
 286    /// </summary>
 287    [JsonIgnore]
 288    [MemoryPackIgnore]
 289    private KeyCollection? _keyCollection;
 290
 291    /// <inheritdoc/>
 292    [JsonIgnore]
 293    [MemoryPackIgnore]
 29294    public ICollection<TKey> Keys => _keyCollection ??= new KeyCollection(this);
 295
 296    [JsonIgnore]
 297    [MemoryPackIgnore]
 8298    ICollection IDictionary.Keys => _keyCollection ??= new KeyCollection(this);
 299
 300    /// <summary>
 301    /// The collection containing the values of the dictionary.
 302    /// </summary>
 303    [JsonIgnore]
 304    [MemoryPackIgnore]
 305    private ValueCollection? _valueCollection;
 306
 307    /// <inheritdoc/>
 308    [JsonIgnore]
 309    [MemoryPackIgnore]
 30310    public ICollection<TValue> Values => _valueCollection ??= new ValueCollection(this);
 311
 312    [JsonIgnore]
 313    [MemoryPackIgnore]
 3314    ICollection IDictionary.Values => _valueCollection ??= new ValueCollection(this);
 315
 316    [JsonIgnore]
 317    [MemoryPackIgnore]
 1318    bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
 319
 320    [JsonIgnore]
 1321    [MemoryPackIgnore] bool IDictionary.IsReadOnly => false;
 1322    bool IDictionary.IsFixedSize => false;
 323
 324    [JsonIgnore]
 325    [MemoryPackIgnore]
 1326    bool ICollection.IsSynchronized => false;
 327
 328    /// <inheritdoc/>
 329    [JsonIgnore]
 330    [MemoryPackIgnore]
 3331    public object SyncRoot => _syncRoot ??= new object();
 332
 333    /// <summary>
 334    /// Gets or sets the current state of the dictionary, including all key-value pairs.
 335    /// </summary>
 336    /// <remarks>
 337    /// The state can be used to serialize or restore the contents of the dictionary.
 338    /// Setting this property replaces the entire contents of the dictionary with the provided state.
 339    /// The setter is intended for internal use and is not accessible to external callers.
 340    /// </remarks>
 341    [JsonInclude]
 342    [MemoryPackInclude]
 343    public SwiftDictionaryState<TKey, TValue> State
 344    {
 345        get
 346        {
 15347            if (_count == 0)
 2348                return new SwiftDictionaryState<TKey, TValue>(Array.Empty<KeyValuePair<TKey, TValue>>());
 349
 13350            var items = new KeyValuePair<TKey, TValue>[_count];
 13351            CopyTo(items, 0);
 352
 13353            return new SwiftDictionaryState<TKey, TValue>(items);
 354        }
 355        internal set
 356        {
 17357            ChronicleGuard.ThrowIfNull(value.Items, nameof(value.Items));
 358
 17359            var items = value.Items;
 17360            int count = items.Length;
 361
 17362            if (count == 0)
 363            {
 3364                Initialize(DefaultCapacity);
 3365                _count = 0;
 3366                _version = 0;
 3367                return;
 368            }
 369
 14370            int size = (int)(count / _LoadFactorThreshold);
 14371            Initialize(size);
 372
 108373            foreach (var kvp in items)
 40374                InsertIfNotExist(kvp.Key, kvp.Value);
 375
 14376            _version = 0;
 14377        }
 378    }
 379
 380    #endregion
 381
 382    #region Collection Manipulation
 383
 384    /// <summary>
 385    /// Attempts to add the specified key and value to the dictionary.
 386    /// </summary>
 387    /// <param name="key">The key of the element to add.</param>
 388    /// <param name="value">The value of the element to add.</param>
 389    /// <returns>
 390    /// true if the key/value pair was added to the dictionary successfully;
 391    /// false if the key already exists.
 392    /// </returns>
 393    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 394    public virtual bool Add(TKey key, TValue value)
 395    {
 23331396        CheckLoadThreshold();
 23331397        return InsertIfNotExist(key, value);
 398    }
 399
 400    /// <inheritdoc/>
 1401    public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
 402
 1403    void IDictionary<TKey, TValue>.Add(TKey key, TValue value) => Add(key, value);
 404
 405    /// <inheritdoc/>
 406    public void Add(object key, object? value)
 407    {
 14408        ChronicleGuard.ThrowIfNullAndNullsAreIllegal(value, default(TValue));
 409
 410        try
 411        {
 14412            TKey tempKey = (TKey)key;
 413            try
 414            {
 12415                Add(tempKey, (TValue)value!);
 9416            }
 2417            catch (InvalidCastException)
 418            {
 2419                throw new ArgumentException($"Value {value} does not match expected {typeof(TValue)}");
 420            }
 9421        }
 2422        catch (InvalidCastException)
 423        {
 2424            throw new ArgumentException($"Key {key} does not match expected {typeof(TKey)}");
 425        }
 9426    }
 427
 428    /// <summary>
 429    /// Inserts a key/value pair into the dictionary. If the key already exists and
 430    /// pair is added, or the method returns false if the key already exists.
 431    /// </summary>
 432    /// <param name="key">The key to insert or update.</param>
 433    /// <param name="value">The value to insert or update.</param>
 434    /// <returns>
 435    /// true if the key/value pair was added to the dictionary successfully;
 436    /// false if the key already exists.
 437    /// </returns>
 438    /// <exception cref="ArgumentNullException">Thrown when the key is null.</exception>
 439    internal virtual bool InsertIfNotExist(TKey key, TValue value)
 440    {
 26811441        ChronicleGuard.ThrowIfNullGeneric(key, nameof(key));
 442
 26805443        int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
 26805444        int entryIndex = hashCode & _entryMask;
 445
 26805446        int firstDeletedIndex = -1;
 26805447        int step = 1;
 448        // Cumulative square offsets repeat after 2 * capacity for power-of-two tables.
 449        // One capacity can revisit buckets before reaching every slot, including after trim.
 26805450        uint probeLimit = (uint)_entries.Length * 2;
 48217451        while ((uint)step <= probeLimit)
 452        {
 48216453            ref Entry entry = ref _entries[entryIndex];
 48216454            if (entry.IsUsed)
 455            {
 20456456                if (entry.HashCode == hashCode && _comparer.Equals(entry.Key, key))
 77457                    return false; // Item already exists
 458            }
 27760459            else if (entry.HashCode == -1)
 460            {
 2065461                if (firstDeletedIndex < 0) firstDeletedIndex = entryIndex;
 462            }
 463            else
 464            {
 465                break;
 466            }
 467
 21412468            entryIndex = (entryIndex + step * step) & _entryMask; // Quadratic probing
 21412469            step++;
 470        }
 471
 26728472        if (firstDeletedIndex >= 0)
 1031473            entryIndex = firstDeletedIndex;
 25697474        else if ((uint)step > probeLimit)
 475        {
 1476            Resize(_entries.Length * _adaptiveResizeFactor);
 1477            return InsertIfNotExist(key, value);
 478        }
 479
 44736480        if ((uint)entryIndex > (uint)_lastIndex) _lastIndex = entryIndex;
 481
 26727482        _entries[entryIndex].HashCode = hashCode;
 26727483        _entries[entryIndex].Key = key;
 26727484        _entries[entryIndex].Value = value;
 26727485        _entries[entryIndex].IsUsed = true;
 26727486        _count++;
 26727487        _version++;
 488
 26727489        if ((uint)step > (uint)_maxStepCount)
 490        {
 2025491            _maxStepCount = step;
 2025492            if (_comparer is not IRandomedEqualityComparer &&
 2025493                _maxStepCount > 100 &&
 2025494                SwiftHashTools.IsWellKnownEqualityComparer(_comparer))
 1495                SwitchToRandomizedComparer();  // Attempt to recompute hash code with potential randomization for better
 496        }
 497
 498
 26727499        return true;
 500    }
 501
 502    /// <inheritdoc/>
 503    public virtual bool Remove(TKey key)
 504    {
 8200505        if (_KeyCanBeNull && key is null) return false;
 506
 8194507        int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
 8194508        int entryIndex = hashCode & _entryMask;
 509
 8194510        int step = 0;
 511        // A bucket index is not a probe distance: wrapped chains can take more steps.
 512        // Use the same complete cumulative-square period as insertion.
 9667513        while ((uint)step < (uint)_entries.Length * 2)
 514        {
 9664515            ref Entry entry = ref _entries[entryIndex];
 516            // Stop probing if an unused entry is found (not deleted)
 9664517            if (!entry.IsUsed && entry.HashCode != -1)
 1026518                return false;
 8638519            if (entry.IsUsed && entry.HashCode == hashCode && _comparer.Equals(entry.Key, key))
 520            {
 521                // Mark entry as deleted
 7165522                entry.IsUsed = false;
 7165523                entry.Key = default!;
 7165524                entry.Value = default!;
 7165525                entry.HashCode = -1;
 7165526                _count--;
 8209527                if ((uint)_count == 0) _lastIndex = 0;
 7165528                _version++;
 7165529                return true;
 530            }
 531
 532            // Move to the next entry using linear probing
 1473533            step++;
 1473534            entryIndex = (entryIndex + step * step) & _entryMask;
 535        }
 3536        return false; // Item not found after full loop
 537    }
 538
 539    void IDictionary.Remove(object obj)
 540    {
 3541        ChronicleGuard.ThrowIfNull(obj, nameof(obj));
 5542        if (obj is TKey key) Remove(key);
 3543    }
 544
 545    /// <inheritdoc/>
 546    public bool Remove(KeyValuePair<TKey, TValue> item)
 547    {
 5548        int index = FindEntry(item.Key);
 5549        if (index >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[index].Value, item.Value))
 550        {
 1551            Remove(item.Key);
 1552            return true;
 553        }
 4554        return false;
 555    }
 556
 557    /// <inheritdoc/>
 558    public virtual void Clear()
 559    {
 1626560        if ((uint)_count == 0) return;
 561
 11240562        for (uint i = 0; i <= (uint)_lastIndex; i++)
 563        {
 564            // Clear is a full reset, not a delete; future probes must be able to stop
 565            // at these now-empty slots instead of treating them as tombstones.
 4286566            _entries[i].HashCode = 0;
 4286567            _entries[i].Key = default!;
 4286568            _entries[i].Value = default!;
 4286569            _entries[i].IsUsed = false;
 570        }
 571
 1334572        _count = 0;
 1334573        _lastIndex = 0;
 1334574        _maxStepCount = 0;
 1334575        _movingFillRate = 0;
 1334576        _adaptiveResizeFactor = 4;
 577
 1334578        _version++;
 1334579    }
 580
 581    /// <summary>
 582    /// Removes all elements, optionally releasing references throughout retained backing storage.
 583    /// </summary>
 584    /// <param name="clearRetainedReferences">If true, also releases references outside the live range; otherwise uses o
 585    /// <remarks>Capacity is preserved. Full reference cleanup can take time proportional to retained capacity.</remarks
 586    public void Clear(bool clearRetainedReferences)
 587    {
 282588        if (clearRetainedReferences && RuntimeHelpers.IsReferenceOrContainsReferences<Entry>())
 589        {
 270590            int start = _count == 0 ? 0 : _lastIndex + 1;
 270591            Array.Clear(_entries, start, _entries.Length - start);
 592        }
 282593        Clear();
 282594    }
 595
 596    #endregion
 597
 598    #region Capacity Management
 599
 600    /// <summary>
 601    /// Ensures that the dictionary is resized when the current load factor exceeds the predefined threshold.
 602    /// </summary>
 603    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 604    protected void CheckLoadThreshold()
 605    {
 26734606        if ((uint)_count >= _nextResizeCount)
 59607            Resize(_entries.Length * _adaptiveResizeFactor);
 26734608    }
 609
 610    /// <summary>
 611    /// Ensures that the dictionary can hold up to the specified number of entries, if not it resizes.
 612    /// </summary>
 613    /// <param name="capacity">The minimum capacity to ensure.</param>
 614    /// <returns>The new capacity of the dictionary.</returns>
 615    /// <exception cref="ArgumentOutOfRangeException">The capacity is less than zero.</exception>
 616    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 617    public void EnsureCapacity(int capacity)
 618    {
 7619        capacity = SwiftHashTools.NextPowerOfTwo(capacity);  // Capacity must be a power of 2 for proper masking
 7620        if (capacity > _entries.Length)
 4621            Resize(capacity);
 7622    }
 623
 624    /// <summary>
 625    /// Resizes the internal arrays to the specified new size.
 626    /// </summary>
 627    /// <param name="newSize">The new size for the internal arrays.</param>
 628    private void Resize(int newSize)
 629    {
 64630        Entry[] newEntries = new Entry[newSize];
 64631        int newMask = newSize - 1;
 632
 64633        int lastIndex = 0;
 35412634        for (uint i = 0; i <= (uint)_lastIndex; i++)
 635        {
 17642636            if (_entries[i].IsUsed) // Only rehash valid entries
 637            {
 16621638                ref Entry oldEntry = ref _entries[i];
 16621639                int newIndex = oldEntry.HashCode & newMask;
 640                // If current entry not available, perform Quadratic probing to find the next available entry
 16621641                int step = 1;
 17284642                while (newEntries[newIndex].IsUsed)
 643                {
 663644                    newIndex = (newIndex + step * step) & newMask;
 663645                    step++;
 646                }
 16621647                newEntries[newIndex] = oldEntry;
 28969648                if (newIndex > lastIndex) lastIndex = newIndex;
 649            }
 650        }
 651
 64652        _lastIndex = lastIndex;
 653
 64654        CalculateAdaptiveResizeFactors(newSize);
 655
 64656        _entries = newEntries;
 64657        _entryMask = newMask;
 658
 64659        _version++;
 64660    }
 661
 662    /// <summary>
 663    /// Sets the capacity of a <see cref="SwiftDictionary{TKey, TValue}"/> to the actual
 664    /// number of elements it contains, rounded up to a nearby next power of 2 value.
 665    /// </summary>
 666    public void TrimExcess()
 667    {
 6668        int newSize = _count <= DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(_count);
 7669        if (newSize >= _entries.Length) return;
 670
 5671        Entry[] newEntries = new Entry[newSize];
 5672        int newMask = newSize - 1;
 673
 5674        int lastIndex = 0;
 478675        for (int i = 0; i <= (uint)_lastIndex; i++)
 676        {
 234677            if (_entries[i].IsUsed)
 678            {
 71679                ref Entry oldEntry = ref _entries[i];
 71680                int newIndex = oldEntry.HashCode & newMask;
 681                // If current entry not available, perform quadratic probing to find the next available entry
 71682                int step = 1;
 1015683                while (newEntries[newIndex].IsUsed)
 684                {
 944685                    newIndex = (newIndex + step * step) & newMask;
 944686                    step++;
 687                }
 71688                newEntries[newIndex] = oldEntry;
 97689                if (newIndex > lastIndex) lastIndex = newIndex;
 690            }
 691        }
 692
 5693        _lastIndex = lastIndex;
 694
 5695        CalculateAdaptiveResizeFactors(newSize);
 696
 5697        _entryMask = newMask;
 5698        _entries = newEntries;
 699
 5700        _version++;
 5701    }
 702
 703    /// <summary>
 704    ///  Updates adaptive resize parameters based on the current fill rate to balance memory usage and performance.
 705    /// </summary>
 706    /// <param name="newSize"></param>
 707    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 708    private void CalculateAdaptiveResizeFactors(int newSize)
 709    {
 710        // Calculate current fill rate and update moving average
 69711        double currentFillRate = (double)_count / newSize;
 69712        _movingFillRate = _movingFillRate == 0 ? currentFillRate : (_movingFillRate * 0.7 + currentFillRate * 0.3);
 713
 69714        if (_movingFillRate > 0.3f)
 5715            _adaptiveResizeFactor = 2; // Growth stabilizing
 64716        else if (_movingFillRate < 0.28f)
 63717            _adaptiveResizeFactor = 4; // Rapid growth
 718
 719        // Reset the resize threshold based on the new size
 69720        _nextResizeCount = (uint)(newSize * _LoadFactorThreshold);
 69721    }
 722
 723    #endregion
 724
 725    #region Utility Methods
 726
 727    /// <summary>
 728    /// Initializes the dictionary with the specified capacity.
 729    /// </summary>
 730    /// <param name="capacity">The initial number of elements that the dictionary can contain.</param>
 731    /// <param name="comparer">The comparer to use for the dictionary.</param>
 732    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 733    [MemberNotNull(nameof(_comparer))]
 734    private void Initialize(int capacity, IEqualityComparer<TKey>? comparer = null)
 735    {
 447736        _comparer = SwiftHashTools.GetDefaultEqualityComparer(comparer);
 737
 447738        int size = capacity < DefaultCapacity ? DefaultCapacity : SwiftHashTools.NextPowerOfTwo(capacity);
 447739        _entries = new Entry[size];
 447740        _entryMask = size - 1;
 741
 447742        _nextResizeCount = (uint)(size * _LoadFactorThreshold);
 447743        _adaptiveResizeFactor = 4; // start agressive
 447744        _movingFillRate = 0.0;
 447745    }
 746
 747    /// <summary>
 748    /// Determines whether the dictionary contains an element with the specified key.
 749    /// </summary>
 750    /// <param name="key">The key to locate in the dictionary.</param>
 751    /// <returns>true if the dictionary contains an element with the specified key; otherwise, false.</returns>
 752    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 19589753    public bool ContainsKey(TKey key) => FindEntry(key) >= 0;
 754
 755    bool IDictionary.Contains(object obj)
 756    {
 5757        ChronicleGuard.ThrowIfNull(obj, nameof(obj));
 758
 9759        if (obj is TKey key) return ContainsKey(key);
 1760        return false;
 761    }
 762
 763    /// <inheritdoc/>
 764    public bool Contains(KeyValuePair<TKey, TValue> item)
 765    {
 4766        int index = FindEntry(item.Key);
 4767        if (index >= 0 && EqualityComparer<TValue>.Default.Equals(_entries[index].Value, item.Value))
 1768            return true;
 3769        return false;
 770    }
 771
 772    /// <inheritdoc/>
 773    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 774    public bool TryGetValue(TKey key, out TValue value)
 775    {
 9095776        int index = FindEntry(key);
 9095777        if (index >= 0)
 778        {
 7322779            value = _entries[index].Value;
 7322780            return true;
 781        }
 1773782        value = default!;
 1773783        return false;
 784    }
 785
 786    /// <summary>
 787    /// Copies the elements of the collection to the specified array, starting at the given array index.
 788    /// </summary>
 789    /// <param name="array">
 790    /// The one-dimensional array of key/value pairs that is the destination of the elements copied from the collection.
 791    /// The array must have zero-based indexing.
 792    /// </param>
 793    /// <param name="arrayIndex">The zero-based index in the destination array at which copying begins.</param>
 794    /// <exception cref="ArgumentOutOfRangeException">Thrown if arrayIndex is less than 0 or greater than the length of 
 795    /// <exception cref="ArgumentException">
 796    /// Thrown if the number of elements in the source collection is greater than the available space from arrayIndex to
 797    /// the end of the destination array.
 798    /// </exception>
 799    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
 800    {
 15801        ChronicleGuard.ThrowIfNull(array, nameof(array));
 15802        ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 15803        ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "Insufficient space in the tar
 804
 250805        for (uint i = 0; i <= (uint)_lastIndex; i++)
 806        {
 110807            if (_entries[i].IsUsed)
 42808                array[arrayIndex++] = new KeyValuePair<TKey, TValue>(_entries[i].Key, _entries[i].Value);
 809        }
 15810    }
 811
 812    /// <inheritdoc/>
 813    public void CopyTo(Array array, int arrayIndex)
 814    {
 10815        ChronicleGuard.ThrowIfNull(array, nameof(array));
 10816        ChronicleGuard.ThrowIfArgument(array.Rank != 1, nameof(array), "Multidimensional array not supported");
 9817        ChronicleGuard.ThrowIfArgument(array.GetLowerBound(0) != 0, nameof(array), "Non-zero lower bound");
 8818        ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 8819        ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _count, nameof(array), "Insufficient space in the tar
 820
 7821        if (array is KeyValuePair<TKey, TValue>[] pairs)
 1822            ((ICollection<KeyValuePair<TKey, TValue>>)this).CopyTo(pairs, arrayIndex);
 6823        else if (array is DictionaryEntry[] dictEntryArray)
 2824            CopyToDictionaryEntries(dictEntryArray, arrayIndex);
 825        else
 4826            CopyToObjects(array, arrayIndex);
 2827    }
 828
 829    private void CopyToDictionaryEntries(DictionaryEntry[] array, int arrayIndex)
 830    {
 16831        for (uint i = 0; i <= (uint)_lastIndex; i++)
 832        {
 6833            if (_entries[i].IsUsed)
 4834                array[arrayIndex++] = new DictionaryEntry(_entries[i].Key, _entries[i].Value);
 835        }
 2836    }
 837
 838    private void CopyToObjects(Array array, int arrayIndex)
 839    {
 4840        if (array is not object[] objects)
 1841            throw new ArgumentException("Invalid array type", nameof(array));
 842
 843        try
 844        {
 3845            CopyToObjects(objects, arrayIndex);
 2846        }
 1847        catch (ArrayTypeMismatchException)
 848        {
 1849            throw new ArgumentException("Invalid array type", nameof(array));
 850        }
 2851    }
 852
 853    private void CopyToObjects(object[] array, int arrayIndex)
 854    {
 20855        for (uint i = 0; i <= (uint)_lastIndex; i++)
 856        {
 8857            if (_entries[i].IsUsed)
 5858                array[arrayIndex++] = new KeyValuePair<TKey, TValue>(_entries[i].Key, _entries[i].Value);
 859        }
 2860    }
 861
 862    /// <summary>
 863    /// Sets a new comparer for the dictionary and rehashes the entries.
 864    /// </summary>
 865    /// <param name="comparer">The new comparer to use.</param>
 866    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 867    public void SetComparer(IEqualityComparer<TKey> comparer)
 868    {
 5869        ChronicleGuard.ThrowIfNull(comparer, nameof(comparer));
 5870        if (ReferenceEquals(comparer, _comparer))
 1871            return;
 872
 4873        _comparer = comparer;
 4874        RehashEntries();
 4875        _maxStepCount = 0;
 4876    }
 877
 878    /// <summary>
 879    /// Switches the dictionary's comparer to a randomized comparer to mitigate the effects of high collision counts,
 880    /// and rehashes all entries using the new comparer to redistribute them across <see cref="_entries"/>.
 881    /// </summary>
 882    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 883    private void SwitchToRandomizedComparer()
 884    {
 1885        _comparer = (IEqualityComparer<TKey>)SwiftHashTools.GetSwiftEqualityComparer(_comparer);
 886
 1887        RehashEntries();
 1888        _maxStepCount = 0;
 889
 1890        _version++;
 1891    }
 892
 893    /// <summary>
 894    /// Reconstructs the internal entry structure to align with updated hash codes, ensuring efficient access and storag
 895    /// </summary>
 896    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 897    private void RehashEntries()
 898    {
 5899        Entry[] newEntries = new Entry[_entries.Length];
 5900        int newMask = newEntries.Length - 1;
 901
 5902        int lastIndex = 0;
 612903        for (uint i = 0; i <= (uint)_lastIndex; i++)
 904        {
 301905            if (_entries[i].IsUsed)
 906            {
 109907                ref Entry oldEntry = ref _entries[i];
 109908                oldEntry.HashCode = _comparer.GetHashCode(oldEntry.Key) & 0x7FFFFFFF;
 109909                int newIndex = oldEntry.HashCode & newMask;
 109910                int step = 1;
 128911                while (newEntries[newIndex].IsUsed)
 912                {
 19913                    newIndex = (newIndex + step * step) & newMask; // Quadratic probing
 19914                    step++;
 915                }
 109916                newEntries[newIndex] = _entries[i];
 132917                if (newIndex > lastIndex) lastIndex = newIndex;
 918            }
 919        }
 920
 5921        _lastIndex = lastIndex;
 922
 5923        _entryMask = newMask;
 5924        _entries = newEntries;
 925
 5926        _version++;
 5927    }
 928
 929    /// <summary>
 930    /// Finds the array index of the entry with the specified key.
 931    /// </summary>
 932    /// <param name="key">The key to locate in the dictionary.</param>
 933    /// <returns>The array index of the entry if found; otherwise, -1, including for a null key.</returns>
 934    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 935    protected int FindEntry(TKey key)
 936    {
 44117937        if (_KeyCanBeNull && key is null) return -1;
 938
 44091939        int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
 44091940        int entryIndex = hashCode & _entryMask;
 941
 44091942        int step = 0;
 943        // A bucket index is not a probe distance: wrapped chains can take more steps.
 944        // Use the same complete cumulative-square period as insertion.
 58822945        while ((uint)step < (uint)_entries.Length * 2)
 946        {
 58819947            ref Entry entry = ref _entries[entryIndex];
 948            // Stop probing if an unused entry is found (not deleted)
 58819949            if (!entry.IsUsed && entry.HashCode != -1)
 19699950                return -1;
 39120951            if (entry.IsUsed && entry.HashCode == hashCode && _comparer.Equals(entry.Key, key))
 24389952                return entryIndex; // Match found
 953
 954            // Perform quadratic probing to see if maybe the entry was shifted.
 14731955            step++;
 14731956            entryIndex = (entryIndex + step * step) & _entryMask;
 957        }
 3958        return -1; // Item not found, full loop completed
 959    }
 960
 961    #endregion
 962
 963    #region IEnumerable Implementation
 964
 965    /// <inheritdoc cref="IEnumerable.GetEnumerator()"/>
 174966    public SwiftDictionaryEnumerator GetEnumerator() => new(this);
 107967    IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() => GetEnumerator();
 55968    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 4969    IDictionaryEnumerator IDictionary.GetEnumerator() => new SwiftDictionaryEnumerator(this, true);
 970
 971
 972
 973    #endregion
 974
 975    #region Key & Value Collections
 976
 977
 978
 979
 980
 981    #endregion
 982}

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftDictionary.KeyCollection.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftDictionary.KeyCollection.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 Chronicler.Diagnostics;
 12
 13namespace SwiftCollections;
 14
 15public partial class SwiftDictionary<TKey, TValue>
 16    where TKey : notnull
 17{
 18    /// <summary>
 19    /// Provides a dynamic, read-only collection of all keys in the dictionary, supporting enumeration and copy operatio
 20    /// </summary>
 21    [Serializable]
 22    public sealed partial class KeyCollection : ICollection<TKey>, ICollection, IReadOnlyCollection<TKey>, IEnumerable<T
 23    {
 24        private readonly SwiftDictionary<TKey, TValue> _dictionary;
 25        private readonly Entry[] _entries;
 26
 27        /// <summary>
 28        /// Initializes a new instance of the KeyCollection class that reflects the keys in the specified dictionary.
 29        /// </summary>
 30        /// <param name="dictionary">The dictionary whose keys are reflected in the new KeyCollection.</param>
 31        /// <exception cref="ArgumentNullException">The dictionary is null.</exception>
 3332        public KeyCollection(SwiftDictionary<TKey, TValue> dictionary)
 33        {
 3334            ChronicleGuard.ThrowIfNull(dictionary, nameof(dictionary));
 3335            _dictionary = dictionary;
 3336            _entries = dictionary._entries;
 3337        }
 38
 39        /// <inheritdoc/>
 140        public int Count => _dictionary._count;
 41
 142        bool ICollection.IsSynchronized => false;
 43
 144        object ICollection.SyncRoot => ((ICollection)_dictionary).SyncRoot;
 45
 146        bool ICollection<TKey>.IsReadOnly => true;
 47
 148        void ICollection<TKey>.Add(TKey item) => throw new NotSupportedException();
 49
 150        void ICollection<TKey>.Clear() => throw new NotSupportedException();
 51
 252        bool ICollection<TKey>.Contains(TKey item) => _dictionary.ContainsKey(item);
 53
 154        bool ICollection<TKey>.Remove(TKey item) => false;
 55
 56        /// <inheritdoc/>
 57        public void CopyTo(TKey[] array, int arrayIndex)
 58        {
 259            ChronicleGuard.ThrowIfNull(array, nameof(array));
 260            ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 261            ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _dictionary._count, nameof(array), "Insufficient 
 62
 3863            for (int i = 0, j = arrayIndex; i < _entries.Length; i++)
 64            {
 1665                if (_entries[i].IsUsed)
 466                    array[j++] = _entries[i].Key;
 67            }
 268        }
 69
 70        void ICollection.CopyTo(Array array, int arrayIndex)
 71        {
 572            ChronicleGuard.ThrowIfNull(array, nameof(array));
 573            ChronicleGuard.ThrowIfArgument(array.Rank != 1, nameof(array), "Multidimensional array not supported");
 574            ChronicleGuard.ThrowIfArgument(array.GetLowerBound(0) != 0, nameof(array), "Non-zero lower bound");
 575            ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 576            ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _dictionary._count, nameof(array), "Insufficient 
 77
 578            if (array is TKey[] keysArray)
 179                CopyTo(keysArray, arrayIndex);
 480            else if (array is object[] objects)
 81            {
 82                try
 83                {
 4384                    for (int i = 0, j = arrayIndex; i < _entries.Length; i++)
 85                    {
 1886                        if (_entries[i].IsUsed)
 587                            objects[j++] = _entries[i].Key;
 88                    }
 289                }
 190                catch (ArrayTypeMismatchException)
 91                {
 192                    throw new ArgumentException("Invalid array type", nameof(array));
 93                }
 94            }
 95            else
 96            {
 197                throw new ArgumentException("Invalid array type", nameof(array));
 98            }
 299        }
 100
 101        /// <summary>
 102        /// Returns an enumerator that iterates through the keys in the collection.
 103        /// </summary>
 104        /// <returns>An enumerator for the keys in the collection.</returns>
 46105        public KeyCollectionEnumerator GetEnumerator() => new(_dictionary);
 25106        IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator() => GetEnumerator();
 21107        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 108
 109
 110    }
 111}

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftDictionary.KeyCollection.KeyCollectionEnumerator.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftDictionary.KeyCollection.KeyCollectionEnumerator.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 Chronicler.Diagnostics;
 12
 13namespace SwiftCollections;
 14
 15public partial class SwiftDictionary<TKey, TValue>
 16    where TKey : notnull
 17{
 18    public sealed partial class KeyCollection
 19    {
 20        /// <summary>
 21        /// Enumerates the keys of a <see cref="SwiftDictionary{TKey, TValue}"/> collection.
 22        /// </summary>
 23        /// <remarks>
 24        /// The enumerator provides read-only, forward-only iteration over the keys in the dictionary.
 25        /// The enumerator is invalidated if the dictionary is modified after the enumerator is created.
 26        /// In such cases, calling MoveNext or Reset will throw an InvalidOperationException.
 27        /// </remarks>
 28        [Serializable]
 29        public struct KeyCollectionEnumerator : IEnumerator<TKey>, IEnumerator, IDisposable
 30        {
 31            private readonly SwiftDictionary<TKey, TValue> _dictionary;
 32            private readonly Entry[] _entries;
 33            private readonly uint _version;
 34            private int _index;
 35            private TKey _currentKey;
 36
 37            internal KeyCollectionEnumerator(SwiftDictionary<TKey, TValue> dictionary)
 38            {
 4639                _dictionary = dictionary;
 4640                _entries = dictionary._entries;
 4641                _version = dictionary._version;
 4642                _index = -1;
 4643                _currentKey = default!;
 4644            }
 45
 46            /// <inheritdoc/>
 4847            public TKey Current => _currentKey;
 48
 49            object IEnumerator.Current
 50            {
 51                get
 52                {
 3353                    ChronicleGuard.ThrowIfTrue((uint)_index > (uint)_dictionary._lastIndex, message: "Enumerator is posi
 1554                    return _currentKey;
 55                }
 56            }
 57
 58            /// <inheritdoc/>
 59            public bool MoveNext()
 60            {
 11061                ChronicleGuard.ThrowIfTrue(_version != _dictionary._version, message: "Enumerator modified outside of en
 62
 28463                while (++_index <= (uint)_dictionary._lastIndex)
 64                {
 24465                    if (_entries[_index].IsUsed)
 66                    {
 6667                        _currentKey = _entries[_index].Key;
 6668                        return true;
 69                    }
 70                }
 71
 4072                _currentKey = default!;
 4073                return false;
 74            }
 75
 76            /// <inheritdoc/>
 77            public void Reset()
 78            {
 1179                ChronicleGuard.ThrowIfTrue(_version != _dictionary._version, message: "Enumerator modified outside of en
 80
 881                _index = -1;
 882                _currentKey = default!;
 883            }
 84
 85            /// <inheritdoc/>
 2886            public void Dispose() => _index = -1;
 87        }
 88    }
 89}

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftDictionary.SwiftDictionaryEnumerator.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftDictionary.SwiftDictionaryEnumerator.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 Chronicler.Diagnostics;
 12
 13namespace SwiftCollections;
 14
 15public partial class SwiftDictionary<TKey, TValue>
 16    where TKey : notnull
 17{
 18    /// <summary>
 19    /// Provides an efficient enumerator for iterating over the key-value pairs in the SwiftDictionary, enabling smooth 
 20    /// </summary>
 21    [Serializable]
 22    public struct SwiftDictionaryEnumerator : IEnumerator<KeyValuePair<TKey, TValue>>, IEnumerator, IDictionaryEnumerato
 23    {
 24        private readonly SwiftDictionary<TKey, TValue> _dictionary;
 25        private readonly Entry[] _entries;
 26        private readonly uint _version;
 27        private readonly bool _returnEntry;
 28        private int _index;
 29        private KeyValuePair<TKey, TValue> _current;
 30
 31        internal SwiftDictionaryEnumerator(SwiftDictionary<TKey, TValue> dictionary, bool returnEntry = false)
 32        {
 17833            _dictionary = dictionary;
 17834            _entries = dictionary._entries;
 17835            _version = dictionary._version;
 17836            _returnEntry = returnEntry;
 17837            _index = -1;
 17838            _current = default;
 17839        }
 40
 41        object IDictionaryEnumerator.Key
 42        {
 43            get
 44            {
 845                ChronicleGuard.ThrowIfTrue((uint)_index > (uint)_dictionary._lastIndex, message: "Enumerator is position
 446                return _current.Key;
 47            }
 48        }
 49
 50        object IDictionaryEnumerator.Value
 51        {
 52            get
 53            {
 654                ChronicleGuard.ThrowIfTrue((uint)_index > (uint)_dictionary._lastIndex, message: "Enumerator is position
 255                return _current.Value!;
 56            }
 57        }
 58
 59        DictionaryEntry IDictionaryEnumerator.Entry
 60        {
 61            get
 62            {
 863                ChronicleGuard.ThrowIfTrue((uint)_index > (uint)_dictionary._lastIndex, message: "Enumerator is position
 364                return new DictionaryEntry(_current.Key, _current.Value);
 65            }
 66        }
 67
 68        /// <inheritdoc/>
 14569        public KeyValuePair<TKey, TValue> Current => _current;
 70
 71        object IEnumerator.Current
 72        {
 73            get
 74            {
 9775                ChronicleGuard.ThrowIfTrue((uint)_index > (uint)_dictionary._lastIndex, message: "Enumerator is position
 3976                return _returnEntry
 3977                    ? new DictionaryEntry(_current.Key, _current.Value)
 3978                    : new KeyValuePair<TKey, TValue>(_current.Key, _current.Value);
 79            }
 80        }
 81
 82        /// <inheritdoc/>
 83        public bool MoveNext()
 84        {
 37285            ChronicleGuard.ThrowIfTrue(_version != _dictionary._version, message: "Enumerator modified outside of enumer
 86
 95187            while (++_index <= (uint)_dictionary._lastIndex)
 88            {
 78189                if (_entries[_index].IsUsed)
 90                {
 19391                    _current = new KeyValuePair<TKey, TValue>(_entries[_index].Key, _entries[_index].Value);
 19392                    return true;
 93                }
 94            }
 95
 17096            _current = default;
 17097            return false;
 98        }
 99
 100        /// <inheritdoc/>
 101        public void Reset()
 102        {
 29103            ChronicleGuard.ThrowIfTrue(_version != _dictionary._version, message: "Enumerator modified outside of enumer
 104
 20105            _index = -1;
 20106            _current = default;
 20107        }
 108
 109        /// <inheritdoc/>
 128110        public void Dispose() => _index = -1;
 111    }
 112}

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftDictionary.ValueCollection.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftDictionary.ValueCollection.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 Chronicler.Diagnostics;
 12
 13namespace SwiftCollections;
 14
 15public partial class SwiftDictionary<TKey, TValue>
 16    where TKey : notnull
 17{
 18    /// <summary>
 19    /// Offers a dynamic, read-only collection of all values in the dictionary, supporting enumeration and copy operatio
 20    /// </summary>
 21    [Serializable]
 22    public sealed partial class ValueCollection : ICollection<TValue>, ICollection, IReadOnlyCollection<TValue>, IEnumer
 23    {
 24        private readonly SwiftDictionary<TKey, TValue> _dictionary;
 25        private readonly Entry[] _entries;
 26
 27        /// <summary>
 28        /// Initializes a new instance of the ValueCollection class that reflects the values in the specified dictionary
 29        /// </summary>
 30        /// <param name="dictionary">The dictionary whose values are reflected in the new ValueCollection.</param>
 31        /// <exception cref="ArgumentNullException">The dictionary is null.</exception>
 2932        public ValueCollection(SwiftDictionary<TKey, TValue> dictionary)
 33        {
 2934            ChronicleGuard.ThrowIfNull(dictionary, nameof(dictionary));
 2935            _dictionary = dictionary;
 2936            _entries = dictionary._entries;
 2937        }
 38
 39        /// <inheritdoc/>
 140        public int Count => _dictionary._count;
 41
 142        bool ICollection<TValue>.IsReadOnly => true;
 43
 144        bool ICollection.IsSynchronized => false;
 45
 146        object ICollection.SyncRoot => ((ICollection)_dictionary).SyncRoot;
 47
 148        void ICollection<TValue>.Add(TValue item) => throw new NotSupportedException();
 49
 150        void ICollection<TValue>.Clear() => throw new NotSupportedException();
 51
 52        bool ICollection<TValue>.Contains(TValue item)
 53        {
 2054            for (uint i = 0; i <= (uint)_dictionary._lastIndex; i++)
 55            {
 956                if (_entries[i].IsUsed && EqualityComparer<TValue>.Default.Equals(_entries[i].Value, item))
 257                    return true;
 58            }
 159            return false;
 60        }
 61
 162        bool ICollection<TValue>.Remove(TValue item) => false;
 63
 64        /// <inheritdoc/>
 65        public void CopyTo(TValue[] array, int arrayIndex)
 66        {
 267            ChronicleGuard.ThrowIfNull(array, nameof(array));
 268            ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 269            ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _dictionary._count, nameof(array), "Insufficient 
 70
 1871            for (int i = 0, j = arrayIndex; i <= _dictionary._lastIndex; i++)
 72            {
 673                if (_dictionary._entries[i].IsUsed)
 474                    array[j++] = _entries[i].Value;
 75            }
 276        }
 77
 78        void ICollection.CopyTo(Array array, int arrayIndex)
 79        {
 580            ChronicleGuard.ThrowIfNull(array, nameof(array));
 581            ChronicleGuard.ThrowIfArgument(array.Rank != 1, nameof(array), "Multidimensional array not supported");
 582            ChronicleGuard.ThrowIfArgument(array.GetLowerBound(0) != 0, nameof(array), "Non-zero lower bound");
 583            ChronicleGuard.ThrowIfArrayIndexInvalid(arrayIndex, array.Length);
 584            ChronicleGuard.ThrowIfArgument(array.Length - arrayIndex < _dictionary._count, nameof(array), "Insufficient 
 85
 586            if (array is TValue[] valuesArray)
 187                CopyTo(valuesArray, arrayIndex);
 488            else if (array is object[] objects)
 89            {
 90                try
 91                {
 2392                    for (int i = 0, j = arrayIndex; i <= _dictionary._lastIndex; i++)
 893                        if (_entries[i].IsUsed)
 594                            objects[j++] = _entries[i].Value!;
 295                }
 196                catch (ArrayTypeMismatchException)
 97                {
 198                    throw new ArgumentException("Invalid array type", nameof(array));
 99                }
 100            }
 1101            else throw new ArgumentException("Invalid array type", nameof(array));
 2102        }
 103
 104        /// <summary>
 105        /// Returns an enumerator that iterates through the values in the collection.
 106        /// </summary>
 107        /// <returns>An enumerator for the values in the collection.</returns>
 42108        public ValueCollectionEnumerator GetEnumerator() => new(_dictionary);
 22109        IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() => GetEnumerator();
 20110        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 111
 112
 113    }
 114}

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Collection/SwiftDictionary.ValueCollection.ValueCollectionEnumerator.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftDictionary.ValueCollection.ValueCollectionEnumerator.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 Chronicler.Diagnostics;
 12
 13namespace SwiftCollections;
 14
 15public partial class SwiftDictionary<TKey, TValue>
 16    where TKey : notnull
 17{
 18    public sealed partial class ValueCollection
 19    {
 20        /// <summary>
 21        /// Enumerates the values in a SwiftDictionary collection.
 22        /// </summary>
 23        /// <remarks>
 24        /// The enumerator is invalidated if the collection is modified after the enumerator is created.
 25        /// Enumerators are typically used in a foreach statement to iterate through the collection values.
 26        /// </remarks>
 27        [Serializable]
 28        public struct ValueCollectionEnumerator : IEnumerator<TValue>, IEnumerator, IDisposable
 29        {
 30            private readonly SwiftDictionary<TKey, TValue> _dictionary;
 31            private readonly Entry[] _entries;
 32            private readonly uint _version;
 33            private int _index;
 34            private TValue _currentValue;
 35
 36            internal ValueCollectionEnumerator(SwiftDictionary<TKey, TValue> dictionary)
 37            {
 4238                _dictionary = dictionary;
 4239                _entries = dictionary._entries;
 4240                _version = dictionary._version;
 4241                _index = -1;
 4242                _currentValue = default!;
 4243            }
 44
 45            /// <inheritdoc/>
 3746            public TValue Current => _currentValue;
 47
 48            object IEnumerator.Current
 49            {
 50                get
 51                {
 3152                    ChronicleGuard.ThrowIfTrue((uint)_index > (uint)_dictionary._lastIndex, message: "Enumerator is posi
 1353                    return _currentValue!;
 54                }
 55            }
 56
 57            /// <inheritdoc/>
 58            public bool MoveNext()
 59            {
 9360                ChronicleGuard.ThrowIfTrue(_version != _dictionary._version, message: "Enumerator modified outside of en
 61
 24462                while (++_index <= (uint)_dictionary._lastIndex)
 63                {
 20964                    if (_entries[_index].IsUsed)
 65                    {
 5466                        _currentValue = _entries[_index].Value;
 5467                        return true;
 68                    }
 69                }
 70
 3571                _currentValue = default!;
 3572                return false;
 73            }
 74
 75            /// <inheritdoc/>
 76            public void Reset()
 77            {
 1078                ChronicleGuard.ThrowIfTrue(_version != _dictionary._version, message: "Enumerator modified outside of en
 79
 780                _index = -1;
 781                _currentValue = default!;
 782            }
 83
 84            /// <inheritdoc/>
 2585            public void Dispose() => _index = -1;
 86        }
 87    }
 88}

Methods/Properties

.cctor()
.ctor()
.ctor(System.Int32,System.Collections.Generic.IEqualityComparer`1<TKey>)
.ctor(System.Collections.Generic.IDictionary`2<TKey,TValue>,System.Collections.Generic.IEqualityComparer`1<TKey>)
.ctor(System.Collections.Generic.IEnumerable`1<System.Collections.Generic.KeyValuePair`2<TKey,TValue>>,System.Collections.Generic.IEqualityComparer`1<TKey>)
.ctor(SwiftCollections.SwiftDictionaryState`2<TKey,TValue>)
get_Count()
get_Capacity()
get_Comparer()
get_Item(TKey)
set_Item(TKey,TValue)
System.Collections.IDictionary.get_Item(System.Object)
System.Collections.IDictionary.set_Item(System.Object,System.Object)
get_Keys()
System.Collections.IDictionary.get_Keys()
get_Values()
System.Collections.IDictionary.get_Values()
System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.get_IsReadOnly()
System.Collections.IDictionary.get_IsReadOnly()
System.Collections.IDictionary.get_IsFixedSize()
System.Collections.ICollection.get_IsSynchronized()
get_SyncRoot()
get_State()
set_State(SwiftCollections.SwiftDictionaryState`2<TKey,TValue>)
Add(TKey,TValue)
Add(System.Collections.Generic.KeyValuePair`2<TKey,TValue>)
System.Collections.Generic.IDictionary<TKey,TValue>.Add(TKey,TValue)
Add(System.Object,System.Object)
InsertIfNotExist(TKey,TValue)
Remove(TKey)
System.Collections.IDictionary.Remove(System.Object)
Remove(System.Collections.Generic.KeyValuePair`2<TKey,TValue>)
Clear()
Clear(System.Boolean)
CheckLoadThreshold()
EnsureCapacity(System.Int32)
Resize(System.Int32)
TrimExcess()
CalculateAdaptiveResizeFactors(System.Int32)
Initialize(System.Int32,System.Collections.Generic.IEqualityComparer`1<TKey>)
ContainsKey(TKey)
System.Collections.IDictionary.Contains(System.Object)
Contains(System.Collections.Generic.KeyValuePair`2<TKey,TValue>)
TryGetValue(TKey,TValue&)
CopyTo(System.Collections.Generic.KeyValuePair`2<TKey,TValue>[],System.Int32)
CopyTo(System.Array,System.Int32)
CopyToDictionaryEntries(System.Collections.DictionaryEntry[],System.Int32)
CopyToObjects(System.Array,System.Int32)
CopyToObjects(System.Object[],System.Int32)
SetComparer(System.Collections.Generic.IEqualityComparer`1<TKey>)
SwitchToRandomizedComparer()
RehashEntries()
FindEntry(TKey)
GetEnumerator()
System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>.GetEnumerator()
System.Collections.IEnumerable.GetEnumerator()
System.Collections.IDictionary.GetEnumerator()
.ctor(SwiftCollections.SwiftDictionary`2<TKey,TValue>)
get_Count()
System.Collections.ICollection.get_IsSynchronized()
System.Collections.ICollection.get_SyncRoot()
System.Collections.Generic.ICollection<TKey>.get_IsReadOnly()
System.Collections.Generic.ICollection<TKey>.Add(TKey)
System.Collections.Generic.ICollection<TKey>.Clear()
System.Collections.Generic.ICollection<TKey>.Contains(TKey)
System.Collections.Generic.ICollection<TKey>.Remove(TKey)
CopyTo(TKey[],System.Int32)
System.Collections.ICollection.CopyTo(System.Array,System.Int32)
GetEnumerator()
System.Collections.Generic.IEnumerable<TKey>.GetEnumerator()
System.Collections.IEnumerable.GetEnumerator()
.ctor(SwiftCollections.SwiftDictionary`2<TKey,TValue>)
get_Current()
System.Collections.IEnumerator.get_Current()
MoveNext()
Reset()
Dispose()
.ctor(SwiftCollections.SwiftDictionary`2<TKey,TValue>,System.Boolean)
System.Collections.IDictionaryEnumerator.get_Key()
System.Collections.IDictionaryEnumerator.get_Value()
System.Collections.IDictionaryEnumerator.get_Entry()
get_Current()
System.Collections.IEnumerator.get_Current()
MoveNext()
Reset()
Dispose()
.ctor(SwiftCollections.SwiftDictionary`2<TKey,TValue>)
get_Count()
System.Collections.Generic.ICollection<TValue>.get_IsReadOnly()
System.Collections.ICollection.get_IsSynchronized()
System.Collections.ICollection.get_SyncRoot()
System.Collections.Generic.ICollection<TValue>.Add(TValue)
System.Collections.Generic.ICollection<TValue>.Clear()
System.Collections.Generic.ICollection<TValue>.Contains(TValue)
System.Collections.Generic.ICollection<TValue>.Remove(TValue)
CopyTo(TValue[],System.Int32)
System.Collections.ICollection.CopyTo(System.Array,System.Int32)
GetEnumerator()
System.Collections.Generic.IEnumerable<TValue>.GetEnumerator()
System.Collections.IEnumerable.GetEnumerator()
.ctor(SwiftCollections.SwiftDictionary`2<TKey,TValue>)
get_Current()
System.Collections.IEnumerator.get_Current()
MoveNext()
Reset()
Dispose()