< Summary

Information
Class: AmbientServices.ConcurrentHashSet.HashSetComparer<T>
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Types/ConcurrentHashSet.cs
Tag: 332_35464845198
Line coverage
100%
Covered lines: 8
Uncovered lines: 0
Coverable lines: 8
Total lines: 361
Line coverage: 100%
Branch coverage
100%
Covered branches: 10
Total branches: 10
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Equals(...)100%88100%
GetHashCode(...)100%22100%

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()
 35        : this(EqualityComparer<T>.Default)
 36    {
 37    }
 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)
 43        : this(collection, EqualityComparer<T>.Default)
 44    {
 45    }
 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>
 50    public ConcurrentHashSet(IEqualityComparer<T> comparer)
 51    {
 52        _dict = new ConcurrentDictionary<T, byte>(comparer);
 53        Comparer = comparer;
 54    }
 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>
 60    public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer)
 61    {
 62        _dict = new ConcurrentDictionary<T, byte>(collection.Select(a => new KeyValuePair<T, byte>(a, 0)), comparer);
 63        Comparer = comparer;
 64    }
 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>
 75    public int Count => _dict.Count;
 76    /// <summary>
 77    /// Gets whether or not the set is empty.
 78    /// </summary>
 79    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>
 85    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>
 91    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>
 96    void ICollection<T>.Add(T item) { _dict.TryAdd(item, 0); }
 97    /// <summary>
 98    /// Clears all items from the set.
 99    /// </summary>
 100    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>
 106    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>
 111    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>
 117    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); }
 119    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    {
 128        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        {
 2136            if (ReferenceEquals(x, y)) return true;
 2137            if (x is null) return false;
 2138            if (y is null) return false;
 2139            return x.IsSubsetOf(y) && y.IsSubsetOf(x);
 140        }
 141        public int GetHashCode(ConcurrentHashSet<T> obj)
 142        {
 2143            int hashcode = 0;
 2144            foreach (T item in obj)
 145            {
 2146                hashcode ^= item.GetHashCode();
 147            }
 2148            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    {
 157        if (other == null) return;
 158        byte junk;
 159        foreach (T item in other)
 160        {
 161            _dict.TryRemove(item, out junk);
 162        }
 163    }
 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    {
 172        if (other == null) { Clear(); return; }
 173        HashSet<T> keep = new(other, Comparer);
 174        foreach (T item in this)
 175        {
 176            if (!keep.Contains(item))
 177            {
 178                Remove(item);
 179            }
 180        }
 181    }
 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    {
 189        if (other == null) return false;
 190        HashSet<T> valid = new(other, Comparer);
 191        if (_dict.Count >= valid.Count) return false;
 192        foreach (T item in this)
 193        {
 194            if (!valid.Contains(item)) return false;
 195        }
 196        return true;
 197    }
 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    {
 205        if (other == null) return !_dict.IsEmpty;
 206        int items = 0;
 207        foreach (T item in other)
 208        {
 209            ++items;
 210            if (!_dict.ContainsKey(item)) return false;
 211        }
 212        if (_dict.Count <= items) return false;
 213        return true;
 214    }
 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    {
 222        if (other == null) return _dict.IsEmpty;
 223        HashSet<T> valid = new(other, Comparer);
 224        if (_dict.Count > valid.Count) return false;
 225        foreach (T item in this)
 226        {
 227            if (!valid.Contains(item)) return false;
 228        }
 229        return true;
 230    }
 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    {
 238        if (other == null) return true;
 239        int items = 0;
 240        foreach (T item in other)
 241        {
 242            ++items;
 243            if (!_dict.ContainsKey(item)) return false;
 244        }
 245        return true;
 246    }
 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    {
 255        if (other == null) return false;
 256        foreach (T item in other)
 257        {
 258            if (_dict.ContainsKey(item)) return true;
 259        }
 260        return false;
 261    }
 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>
 267    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    {
 275        if (match == null) return 0;
 276        int removed = 0;
 277        foreach (T item in this)
 278        {
 279            if (match(item))
 280            {
 281                Remove(item);
 282                ++removed;
 283            }
 284        }
 285        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    {
 294        if (other == null) return _dict.IsEmpty;
 295        int items = 0;
 296        foreach (T item in other)
 297        {
 298            ++items;
 299            if (!_dict.ContainsKey(item)) return false;
 300        }
 301        return _dict.Count == items;
 302    }
 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    {
 309        if (other == null) return;
 310        foreach (T item in other)
 311        {
 312            if (_dict.ContainsKey(item))
 313            {
 314                Remove(item);
 315            }
 316            else
 317            {
 318                Add(item);
 319            }
 320        }
 321    }
 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    {
 329        if (other == null) return;
 330        foreach (T item in other)
 331        {
 332            Add(item);
 333        }
 334    }
 335    /// <summary>
 336    /// Gets whether or not this set is readonly.
 337    /// </summary>
 338    public bool IsReadOnly => false;
 339
 340    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 341    {
 342        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    {
 350        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    {
 359        return "{" + string.Join(",", this.Take(20)) + "}";
 360    }
 361}