< Summary

Information
Class: AmbientServices.ConcurrentHashSet<T>
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Types/ConcurrentHashSet.cs
Tag: 332_35464845198
Line coverage
100%
Covered lines: 96
Uncovered lines: 0
Coverable lines: 96
Total lines: 361
Line coverage: 100%
Branch coverage
100%
Covered branches: 70
Total branches: 70
Branch coverage: 100%
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%11100%
.ctor(...)100%22100%
get_Count()100%11100%
get_IsEmpty()100%11100%
Add(...)100%11100%
TryAdd(...)100%11100%
System.Collections.Generic.ICollection<T>.Add(...)100%11100%
Clear()100%11100%
Contains(...)100%11100%
CopyTo(...)100%11100%
CopyTo(...)100%11100%
.cctor()100%11100%
CreateSetComparer()100%11100%
ExceptWith(...)100%44100%
IntersectWith(...)100%66100%
IsProperSubsetOf(...)100%88100%
IsProperSupersetOf(...)100%88100%
IsSubsetOf(...)100%88100%
IsSupersetOf(...)100%66100%
Overlaps(...)100%66100%
Remove(...)100%11100%
RemoveWhere(...)100%66100%
SetEquals(...)100%66100%
SymmetricExceptWith(...)100%66100%
UnionWith(...)100%44100%
get_IsReadOnly()100%11100%
System.Collections.IEnumerable.GetEnumerator()100%11100%
GetEnumerator()100%11100%
ToString()100%11100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/Types/ConcurrentHashSet.cs

#LineLine coverage
 1using System;
 2using System.Collections.Concurrent;
 3using System.Collections.Generic;
 4using System.Linq;
 5
 6namespace AmbientServices;
 7
 8/// <summary>
 9/// A non-blocking version of <see cref="HashSet{T}"/>.
 10/// </summary>
 11/// <remarks>
 12/// <pitch>A thread-safe <see cref="ISet{T}"/> for registries and subscriber lists that are read and mutated concurrentl
 13/// <pledge>
 14/// Individual operations (<see cref="Add"/>, <see cref="Remove"/>, <see cref="Contains"/>, <see cref="Clear"/>, enumera
 15/// Enumeration is safe during concurrent mutation and represents a moment-in-time-ish view: items added or removed mid-
 16/// Null items are not supported, and items should follow the usual hashed-container rule of stable hash codes while con
 17/// </pledge>
 18/// <plan>A thin adapter over <see cref="ConcurrentDictionary{TKey, TValue}"/> with items as keys and ignored byte value
 19/// <priority>
 20/// 1. Never blocking a caller over atomic set algebra: the single-item operations are safe and lock-free, and the <see 
 21/// 2. Enumeration that never throws over a coherent snapshot: enumerating during concurrent mutation is safe and shows 
 22/// 3. Concurrency safety over memory and set-algebra speed: one <see cref="ConcurrentDictionary{TKey, TValue}"/> entry 
 23/// </priority>
 24/// </remarks>
 25#pragma warning disable CA1710  // we're following the precedent set by the framework itself rather than the code analyz
 26public class ConcurrentHashSet<T> : /* ISerializable, IDeserializationCallback, */ ISet<T>, ICollection<T>, IEnumerable<
 27#pragma warning restore CA1710
 28    where T : notnull // ConcurrentDictionary<,> requires this
 29{
 30    private readonly ConcurrentDictionary<T, byte> _dict;
 31    /// <summary>
 32    /// Constructs an empty ConcurrentHashSet.
 33    /// </summary>
 34    public ConcurrentHashSet()
 335        : this(EqualityComparer<T>.Default)
 36    {
 337    }
 38    /// <summary>
 39    /// Constructs a ConcurrentHashSet with the specified items in it.
 40    /// </summary>
 41    /// <param name="collection">An enumeration of items to initialize the set with.</param>
 42    public ConcurrentHashSet(IEnumerable<T> collection)
 243        : this(collection, EqualityComparer<T>.Default)
 44    {
 245    }
 46    /// <summary>
 47    /// Constructs a ConcurrentHashSet with the specified item comparer.
 48    /// </summary>
 49    /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> to use to compare items in the set.</param>
 350    public ConcurrentHashSet(IEqualityComparer<T> comparer)
 51    {
 352        _dict = new ConcurrentDictionary<T, byte>(comparer);
 353        Comparer = comparer;
 354    }
 55    /// <summary>
 56    /// Constructs a ConcurrentHashSet with the specified item comparer and the specified items in it.
 57    /// </summary>
 58    /// <param name="collection">An enumeration of items to initialize the set with.</param>
 59    /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> to use to compare items in the set.</param>
 260    public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer)
 61    {
 262        _dict = new ConcurrentDictionary<T, byte>(collection.Select(a => new KeyValuePair<T, byte>(a, 0)), comparer);
 263        Comparer = comparer;
 264    }
 65    //protected ConcurrentHashSet(SerializationInfo info, StreamingScope scope)
 66    //{
 67    //}
 68    /// <summary>
 69    /// Gets the item comparer used for this set.
 70    /// </summary>
 71    public IEqualityComparer<T> Comparer { get; }
 72    /// <summary>
 73    /// Gets the number of items currently in this set.
 74    /// </summary>
 275    public int Count => _dict.Count;
 76    /// <summary>
 77    /// Gets whether or not the set is empty.
 78    /// </summary>
 279    public bool IsEmpty => _dict.IsEmpty;
 80    /// <summary>
 81    /// Adds the specified item to the set if it is not already there.
 82    /// </summary>
 83    /// <param name="item">The item to add to the set.</param>
 84    /// <returns>Whether or not the item was added (as opposed to it already being there).</returns>
 385    public bool Add(T item) { return _dict.TryAdd(item, 0); }
 86    /// <summary>
 87    /// Adds the specified item to the set if it is not already there.
 88    /// </summary>
 89    /// <param name="item">The item to add to the set.</param>
 90    /// <returns>Whether or not the item was added (as opposed to it already being there).</returns>
 291    public bool TryAdd(T item) { return _dict.TryAdd(item, 0); }
 92    /// <summary>
 93    /// Adds the specified item to the set if it is not already there.
 94    /// </summary>
 95    /// <param name="item">The item to add to the set.</param>
 296    void ICollection<T>.Add(T item) { _dict.TryAdd(item, 0); }
 97    /// <summary>
 98    /// Clears all items from the set.
 99    /// </summary>
 2100    public void Clear() { _dict.Clear(); }
 101    /// <summary>
 102    /// Checks to see whether or not the specified item is in the set.
 103    /// </summary>
 104    /// <param name="item">The item to look for.</param>
 105    /// <returns><b>true</b> if the item is in the set, otherwise <b>false</b>.</returns>
 2106    public bool Contains(T item) { return _dict.ContainsKey(item); }
 107    /// <summary>
 108    /// Copies all the items in the set into the specified array.
 109    /// </summary>
 110    /// <param name="array">The array to copy items into.</param>
 2111    public void CopyTo(T[] array) { _dict.Keys.CopyTo(array, 0); }
 112    /// <summary>
 113    /// Copies all the items in the set into the specified array starting at the specified location.
 114    /// </summary>
 115    /// <param name="array">The array to copy items into.</param>
 116    /// <param name="arrayIndex">The offset within the array where the first item is to be placed.</param>
 2117    public void CopyTo(T[] array, int arrayIndex) { _dict.Keys.CopyTo(array, arrayIndex); }
 118//        public void CopyTo(T[] array, int arrayIndex, int count) { _dict.Keys.CopyTo(array, arrayIndex, count); }
 2119    private static readonly HashSetComparer _DefaultComparer = new();
 120
 121#pragma warning disable CA1000  // not sure how else this could possibly be accomplished?
 122    /// <summary>
 123    /// Creates a <see cref="IEqualityComparer{T}"/> for comparing sets (using the default item comparer).
 124    /// </summary>
 125    /// <returns>An <see cref="IEqualityComparer{T}"/></returns>
 126    public static IEqualityComparer<ConcurrentHashSet<T>> CreateSetComparer()
 127    {
 2128        return _DefaultComparer;
 129    }
 130#pragma warning restore CA1000
 131
 132    private class HashSetComparer : IEqualityComparer<ConcurrentHashSet<T>>
 133    {
 134        public bool Equals(ConcurrentHashSet<T>? x, ConcurrentHashSet<T>? y)
 135        {
 136            if (ReferenceEquals(x, y)) return true;
 137            if (x is null) return false;
 138            if (y is null) return false;
 139            return x.IsSubsetOf(y) && y.IsSubsetOf(x);
 140        }
 141        public int GetHashCode(ConcurrentHashSet<T> obj)
 142        {
 143            int hashcode = 0;
 144            foreach (T item in obj)
 145            {
 146                hashcode ^= item.GetHashCode();
 147            }
 148            return hashcode;
 149        }
 150    }
 151    /// <summary>
 152    /// Removes all elements in the specified collection from the current set.
 153    /// </summary>
 154    /// <param name="other">An enumeration of items to remove from this set.</param>
 155    public void ExceptWith(IEnumerable<T>? other)
 156    {
 2157        if (other == null) return;
 158        byte junk;
 2159        foreach (T item in other)
 160        {
 2161            _dict.TryRemove(item, out junk);
 162        }
 2163    }
 164    //[SecurityCritical]
 165    //public virtual void GetObjectData(SerializationInfo info, StreamingScope scope);
 166    /// <summary>
 167    /// Modifies the current set to contain only elements that are present in that object and in the specified collectio
 168    /// </summary>
 169    /// <param name="other">An enumeration of items to keep.</param>
 170    public void IntersectWith(IEnumerable<T>? other)
 171    {
 2172        if (other == null) { Clear(); return; }
 2173        HashSet<T> keep = new(other, Comparer);
 2174        foreach (T item in this)
 175        {
 2176            if (!keep.Contains(item))
 177            {
 2178                Remove(item);
 179            }
 180        }
 2181    }
 182    /// <summary>
 183    /// Determines whether this set is a proper subset of the specified collection.
 184    /// </summary>
 185    /// <param name="other">An enumeration of items to compare to.</param>
 186    /// <returns><b>true</b> if this set is a proper subset of the specified collection.</returns>
 187    public bool IsProperSubsetOf(IEnumerable<T>? other)
 188    {
 2189        if (other == null) return false;
 2190        HashSet<T> valid = new(other, Comparer);
 2191        if (_dict.Count >= valid.Count) return false;
 2192        foreach (T item in this)
 193        {
 2194            if (!valid.Contains(item)) return false;
 195        }
 2196        return true;
 2197    }
 198    /// <summary>
 199    /// Determines whether this set is a proper superset of the specified collection.
 200    /// </summary>
 201    /// <param name="other">An enumeration of items to compare to.</param>
 202    /// <returns><b>true</b> if this set is a proper superset of the specified collection.</returns>
 203    public bool IsProperSupersetOf(IEnumerable<T>? other)
 204    {
 2205        if (other == null) return !_dict.IsEmpty;
 2206        int items = 0;
 2207        foreach (T item in other)
 208        {
 2209            ++items;
 2210            if (!_dict.ContainsKey(item)) return false;
 211        }
 2212        if (_dict.Count <= items) return false;
 2213        return true;
 2214    }
 215    /// <summary>
 216    /// Determines whether this set is a subset of the specified collection.
 217    /// </summary>
 218    /// <param name="other">An enumeration of items to compare to.</param>
 219    /// <returns><b>true</b> if this set is a subset of the specified collection.</returns>
 220    public bool IsSubsetOf(IEnumerable<T>? other)
 221    {
 2222        if (other == null) return _dict.IsEmpty;
 2223        HashSet<T> valid = new(other, Comparer);
 2224        if (_dict.Count > valid.Count) return false;
 2225        foreach (T item in this)
 226        {
 2227            if (!valid.Contains(item)) return false;
 228        }
 2229        return true;
 2230    }
 231    /// <summary>
 232    /// Determines whether this set is a superset of the specified collection.
 233    /// </summary>
 234    /// <param name="other">An enumeration of items to compare to.</param>
 235    /// <returns><b>true</b> if this set is a superset of the specified collection.</returns>
 236    public bool IsSupersetOf(IEnumerable<T>? other)
 237    {
 2238        if (other == null) return true;
 2239        int items = 0;
 2240        foreach (T item in other)
 241        {
 2242            ++items;
 2243            if (!_dict.ContainsKey(item)) return false;
 244        }
 2245        return true;
 2246    }
 247    //        public virtual void OnDeserialization(object sender);
 248    /// <summary>
 249    /// Checks to see whether or not there are any items common between this set and the specified collection.
 250    /// </summary>
 251    /// <param name="other">An enumeration of items to compare to.</param>
 252    /// <returns><b>true</b> if there is at least one item that exists in both this set and the specified collection.</r
 253    public bool Overlaps(IEnumerable<T>? other)
 254    {
 2255        if (other == null) return false;
 2256        foreach (T item in other)
 257        {
 2258            if (_dict.ContainsKey(item)) return true;
 259        }
 2260        return false;
 2261    }
 262    /// <summary>
 263    /// Removes an item from the set.
 264    /// </summary>
 265    /// <param name="item">The item to remove from the set.</param>
 266    /// <returns>Whether or not the item was removed (it may not have been there to begin with).</returns>
 3267    public bool Remove(T item) { byte junk; return _dict.TryRemove(item, out junk); }
 268    /// <summary>
 269    /// Removes all items in the set that match the specified predicate.
 270    /// </summary>
 271    /// <param name="match">A <see cref="Predicate{T}"/> to use to evaluate each item in the set.</param>
 272    /// <returns>The number of items removed from the set.</returns>
 273    public int RemoveWhere(Predicate<T>? match)
 274    {
 2275        if (match == null) return 0;
 2276        int removed = 0;
 2277        foreach (T item in this)
 278        {
 2279            if (match(item))
 280            {
 2281                Remove(item);
 2282                ++removed;
 283            }
 284        }
 2285        return removed;
 286    }
 287    /// <summary>
 288    /// Checks to see whether or not this set contains exactly the same items as the specified collection.
 289    /// </summary>
 290    /// <param name="other">An enumeration of items to compare to.</param>
 291    /// <returns>Whether or not this set contains exactly the same items as the specified collection.</returns>
 292    public bool SetEquals(IEnumerable<T>? other)
 293    {
 2294        if (other == null) return _dict.IsEmpty;
 2295        int items = 0;
 2296        foreach (T item in other)
 297        {
 2298            ++items;
 2299            if (!_dict.ContainsKey(item)) return false;
 300        }
 2301        return _dict.Count == items;
 2302    }
 303    /// <summary>
 304    /// Modifies the current set to contain only elements that are present either in that object or in the specified col
 305    /// </summary>
 306    /// <param name="other">An enumeration of items to compare to.</param>
 307    public void SymmetricExceptWith(IEnumerable<T>? other)
 308    {
 2309        if (other == null) return;
 2310        foreach (T item in other)
 311        {
 2312            if (_dict.ContainsKey(item))
 313            {
 2314                Remove(item);
 315            }
 316            else
 317            {
 2318                Add(item);
 319            }
 320        }
 2321    }
 322    //        public void TrimExcess() {  }
 323    /// <summary>
 324    /// Modifies the current set to contain all elements that are present in itself, the specified collection, or both.
 325    /// </summary>
 326    /// <param name="other">An enumeration of items to compare to.</param>
 327    public void UnionWith(IEnumerable<T>? other)
 328    {
 2329        if (other == null) return;
 2330        foreach (T item in other)
 331        {
 2332            Add(item);
 333        }
 2334    }
 335    /// <summary>
 336    /// Gets whether or not this set is readonly.
 337    /// </summary>
 2338    public bool IsReadOnly => false;
 339
 340    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 341    {
 2342        return _dict.Keys.GetEnumerator();
 343    }
 344    /// <summary>
 345    /// Gets an <see cref="IEnumerator{T}"/> that can be used to enumerate items in this set.
 346    /// </summary>
 347    /// <returns>An <see cref="IEnumerator{T}"/> that can be used to enumerate items in this set.</returns>
 348    public IEnumerator<T> GetEnumerator()
 349    {
 3350        return _dict.Keys.GetEnumerator();
 351    }
 352
 353    /// <summary>
 354    /// Gets a string representation of this instance.
 355    /// </summary>
 356    /// <returns>A string representation of this instance.</returns>
 357    public override string ToString()
 358    {
 2359        return "{" + string.Join(",", this.Take(20)) + "}";
 360    }
 361}