From 36a78b55a470f7d5463b421060fea48f37ec359a Mon Sep 17 00:00:00 2001 From: Archi Date: Wed, 21 Feb 2024 03:46:25 +0100 Subject: [PATCH] Good idea --- .../Collections/ConcurrentHashSet.cs | 53 ++++++++++++++++-- ArchiSteamFarm/Collections/ConcurrentList.cs | 22 +++++++- .../Collections/FixedSizeConcurrentQueue.cs | 11 ++-- .../ObservableConcurrentDictionary.cs | 55 ++++++++++++++++--- 4 files changed, 122 insertions(+), 19 deletions(-) diff --git a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs index 7dd961860..4c09f02af 100644 --- a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs +++ b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs @@ -46,6 +46,8 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no } public bool Add(T item) { + ArgumentNullException.ThrowIfNull(item); + if (!BackingCollection.TryAdd(item, true)) { return false; } @@ -65,9 +67,18 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no OnModified?.Invoke(this, EventArgs.Empty); } - public bool Contains(T item) => BackingCollection.ContainsKey(item); + public bool Contains(T item) { + ArgumentNullException.ThrowIfNull(item); - public void CopyTo(T[] array, int arrayIndex) => BackingCollection.Keys.CopyTo(array, arrayIndex); + return BackingCollection.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + ArgumentNullException.ThrowIfNull(array); + ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex); + + BackingCollection.Keys.CopyTo(array, arrayIndex); + } public void ExceptWith(IEnumerable other) { ArgumentNullException.ThrowIfNull(other); @@ -80,6 +91,8 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no public IEnumerator GetEnumerator() => BackingCollection.Keys.GetEnumerator(); public void IntersectWith(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); foreach (T item in this.Where(item => !otherSet.Contains(item))) { @@ -88,36 +101,48 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no } public bool IsProperSubsetOf(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); return (otherSet.Count > Count) && IsSubsetOf(otherSet); } public bool IsProperSupersetOf(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); return (otherSet.Count < Count) && IsSupersetOf(otherSet); } public bool IsSubsetOf(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); return this.All(otherSet.Contains); } public bool IsSupersetOf(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); return otherSet.All(Contains); } public bool Overlaps(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); return otherSet.Any(Contains); } public bool Remove(T item) { + ArgumentNullException.ThrowIfNull(item); + if (!BackingCollection.TryRemove(item, out _)) { return false; } @@ -128,12 +153,16 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no } public bool SetEquals(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); return (otherSet.Count == Count) && otherSet.All(Contains); } public void SymmetricExceptWith(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + ISet otherSet = other as ISet ?? other.ToHashSet(); HashSet removed = []; @@ -155,12 +184,18 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no } } - void ICollection.Add(T item) => Add(item); + void ICollection.Add(T item) { + ArgumentNullException.ThrowIfNull(item); + + Add(item); + } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); [PublicAPI] public bool AddRange(IEnumerable items) { + ArgumentNullException.ThrowIfNull(items); + bool result = false; foreach (T _ in items.Where(Add)) { @@ -172,6 +207,8 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no [PublicAPI] public bool RemoveRange(IEnumerable items) { + ArgumentNullException.ThrowIfNull(items); + bool result = false; foreach (T _ in items.Where(Remove)) { @@ -182,10 +219,16 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no } [PublicAPI] - public int RemoveWhere(Predicate match) => BackingCollection.Keys.Where(match.Invoke).Count(key => BackingCollection.TryRemove(key, out _)); + public int RemoveWhere(Predicate match) { + ArgumentNullException.ThrowIfNull(match); + + return BackingCollection.Keys.Where(match.Invoke).Count(key => BackingCollection.TryRemove(key, out _)); + } [PublicAPI] public bool ReplaceIfNeededWith(IReadOnlyCollection other) { + ArgumentNullException.ThrowIfNull(other); + if (SetEquals(other)) { return false; } @@ -197,6 +240,8 @@ public sealed class ConcurrentHashSet : IReadOnlySet, ISet where T : no [PublicAPI] public void ReplaceWith(IEnumerable other) { + ArgumentNullException.ThrowIfNull(other); + Clear(); UnionWith(other); } diff --git a/ArchiSteamFarm/Collections/ConcurrentList.cs b/ArchiSteamFarm/Collections/ConcurrentList.cs index 6b40ed07e..4cfe6667c 100644 --- a/ArchiSteamFarm/Collections/ConcurrentList.cs +++ b/ArchiSteamFarm/Collections/ConcurrentList.cs @@ -27,7 +27,7 @@ using Nito.AsyncEx; namespace ArchiSteamFarm.Collections; -public sealed class ConcurrentList : IList, IReadOnlyList { +public sealed class ConcurrentList : IList, IReadOnlyList where T : notnull { [PublicAPI] public event EventHandler? OnModified; @@ -55,6 +55,8 @@ public sealed class ConcurrentList : IList, IReadOnlyList { } set { + ArgumentNullException.ThrowIfNull(value); + using (Lock.WriterLock()) { BackingCollection[index] = value; } @@ -64,6 +66,8 @@ public sealed class ConcurrentList : IList, IReadOnlyList { } public void Add(T item) { + ArgumentNullException.ThrowIfNull(item); + using (Lock.WriterLock()) { BackingCollection.Add(item); } @@ -80,12 +84,17 @@ public sealed class ConcurrentList : IList, IReadOnlyList { } public bool Contains(T item) { + ArgumentNullException.ThrowIfNull(item); + using (Lock.ReaderLock()) { return BackingCollection.Contains(item); } } public void CopyTo(T[] array, int arrayIndex) { + ArgumentNullException.ThrowIfNull(array); + ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex); + using (Lock.ReaderLock()) { BackingCollection.CopyTo(array, arrayIndex); } @@ -94,12 +103,17 @@ public sealed class ConcurrentList : IList, IReadOnlyList { public IEnumerator GetEnumerator() => new ConcurrentEnumerator(BackingCollection, Lock.ReaderLock()); public int IndexOf(T item) { + ArgumentNullException.ThrowIfNull(item); + using (Lock.ReaderLock()) { return BackingCollection.IndexOf(item); } } public void Insert(int index, T item) { + ArgumentOutOfRangeException.ThrowIfNegative(index); + ArgumentNullException.ThrowIfNull(item); + using (Lock.WriterLock()) { BackingCollection.Insert(index, item); } @@ -108,6 +122,8 @@ public sealed class ConcurrentList : IList, IReadOnlyList { } public bool Remove(T item) { + ArgumentNullException.ThrowIfNull(item); + using (Lock.WriterLock()) { if (!BackingCollection.Remove(item)) { return false; @@ -120,6 +136,8 @@ public sealed class ConcurrentList : IList, IReadOnlyList { } public void RemoveAt(int index) { + ArgumentOutOfRangeException.ThrowIfNegative(index); + using (Lock.WriterLock()) { BackingCollection.RemoveAt(index); } @@ -130,6 +148,8 @@ public sealed class ConcurrentList : IList, IReadOnlyList { IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); internal void ReplaceWith(IEnumerable collection) { + ArgumentNullException.ThrowIfNull(collection); + using (Lock.WriterLock()) { BackingCollection.Clear(); BackingCollection.AddRange(collection); diff --git a/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs b/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs index 374a13460..137541ec0 100644 --- a/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs +++ b/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs @@ -23,22 +23,17 @@ using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using ArchiSteamFarm.Core; namespace ArchiSteamFarm.Collections; -internal sealed class FixedSizeConcurrentQueue : IEnumerable { +internal sealed class FixedSizeConcurrentQueue : IEnumerable where T : notnull { private readonly ConcurrentQueue BackingQueue = new(); internal byte MaxCount { get => BackingMaxCount; set { - if (value == 0) { - ASF.ArchiLogger.LogNullError(value); - - return; - } + ArgumentOutOfRangeException.ThrowIfZero(value); BackingMaxCount = value; @@ -58,6 +53,8 @@ internal sealed class FixedSizeConcurrentQueue : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); internal void Enqueue(T obj) { + ArgumentNullException.ThrowIfNull(obj); + BackingQueue.Enqueue(obj); Resize(); diff --git a/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs b/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs index f99151eee..395587ac2 100644 --- a/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs +++ b/ArchiSteamFarm/Collections/ObservableConcurrentDictionary.cs @@ -54,6 +54,8 @@ public sealed class ObservableConcurrentDictionary : IDictionary BackingDictionary[key]; set { + ArgumentNullException.ThrowIfNull(value); + if (BackingDictionary.TryGetValue(key, out TValue? savedValue) && EqualityComparer.Default.Equals(savedValue, value)) { return; } @@ -69,7 +71,11 @@ public sealed class ObservableConcurrentDictionary : IDictionary TryAdd(key, value); + public void Add(TKey key, TValue value) { + ArgumentNullException.ThrowIfNull(key); + + TryAdd(key, value); + } public void Clear() { if (BackingDictionary.IsEmpty) { @@ -81,7 +87,14 @@ public sealed class ObservableConcurrentDictionary : IDictionary item) => ((ICollection>) BackingDictionary).Contains(item); - public void CopyTo(KeyValuePair[] array, int arrayIndex) => ((ICollection>) BackingDictionary).CopyTo(array, arrayIndex); + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + ArgumentNullException.ThrowIfNull(array); + ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex); + + ((ICollection>) BackingDictionary).CopyTo(array, arrayIndex); + } + public IEnumerator> GetEnumerator() => BackingDictionary.GetEnumerator(); public bool Remove(KeyValuePair item) { @@ -97,6 +110,8 @@ public sealed class ObservableConcurrentDictionary : IDictionary : IDictionary.ContainsKey(TKey key) => BackingDictionary.ContainsKey(key); - bool IReadOnlyDictionary.ContainsKey(TKey key) => BackingDictionary.ContainsKey(key); + bool IDictionary.ContainsKey(TKey key) { + ArgumentNullException.ThrowIfNull(key); + + return BackingDictionary.ContainsKey(key); + } + + bool IReadOnlyDictionary.ContainsKey(TKey key) { + ArgumentNullException.ThrowIfNull(key); + + return BackingDictionary.ContainsKey(key); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - bool IReadOnlyDictionary.TryGetValue(TKey key, out TValue value) => BackingDictionary.TryGetValue(key, out value!); - bool IDictionary.TryGetValue(TKey key, out TValue value) => BackingDictionary.TryGetValue(key, out value!); + + bool IReadOnlyDictionary.TryGetValue(TKey key, out TValue value) { + ArgumentNullException.ThrowIfNull(key); + + return BackingDictionary.TryGetValue(key, out value!); + } + + bool IDictionary.TryGetValue(TKey key, out TValue value) { + ArgumentNullException.ThrowIfNull(key); + + return BackingDictionary.TryGetValue(key, out value!); + } [PublicAPI] public bool TryAdd(TKey key, TValue value) { + ArgumentNullException.ThrowIfNull(key); + if (!BackingDictionary.TryAdd(key, value)) { return false; } @@ -124,5 +161,9 @@ public sealed class ObservableConcurrentDictionary : IDictionary BackingDictionary.TryGetValue(key, out value); + public bool TryGetValue(TKey key, out TValue? value) { + ArgumentNullException.ThrowIfNull(key); + + return BackingDictionary.TryGetValue(key, out value); + } }