// _ _ _ ____ _ _____ // / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ // / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ // / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | // /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| // // Copyright 2015-2018 Łukasz "JustArchi" Domeradzki // Contact: JustArchi@JustArchi.net // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace ArchiSteamFarm { internal sealed class ConcurrentHashSet : IReadOnlyCollection, ISet { public int Count => BackingCollection.Count; public bool IsReadOnly => false; private readonly ConcurrentDictionary BackingCollection = new ConcurrentDictionary(); public bool Add(T item) => BackingCollection.TryAdd(item, true); public void Clear() => BackingCollection.Clear(); [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] public bool Contains(T item) => BackingCollection.ContainsKey(item); public void CopyTo(T[] array, int arrayIndex) => BackingCollection.Keys.CopyTo(array, arrayIndex); public void ExceptWith(IEnumerable other) { foreach (T item in other) { Remove(item); } } public IEnumerator GetEnumerator() => BackingCollection.Keys.GetEnumerator(); public void IntersectWith(IEnumerable other) { ICollection collection = other as ICollection ?? other.ToList(); foreach (T item in this.Where(item => !collection.Contains(item))) { Remove(item); } } public bool IsProperSubsetOf(IEnumerable other) { ICollection collection = other as ICollection ?? other.ToList(); return (collection.Count != Count) && IsSubsetOf(collection); } public bool IsProperSupersetOf(IEnumerable other) { ICollection collection = other as ICollection ?? other.ToList(); return (collection.Count != Count) && IsSupersetOf(collection); } public bool IsSubsetOf(IEnumerable other) { ICollection collection = other as ICollection ?? other.ToList(); return this.AsParallel().All(collection.Contains); } public bool IsSupersetOf(IEnumerable other) => other.AsParallel().All(Contains); public bool Overlaps(IEnumerable other) => other.AsParallel().Any(Contains); [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] public bool Remove(T item) => BackingCollection.TryRemove(item, out _); public bool SetEquals(IEnumerable other) { ICollection collection = other as ICollection ?? other.ToList(); return (collection.Count == Count) && collection.AsParallel().All(Contains); } public void SymmetricExceptWith(IEnumerable other) { ICollection collection = other as ICollection ?? other.ToList(); HashSet removed = new HashSet(); foreach (T item in collection.Where(Contains)) { removed.Add(item); Remove(item); } foreach (T item in collection.Where(item => !removed.Contains(item))) { Add(item); } } public void UnionWith(IEnumerable other) { foreach (T otherElement in other) { Add(otherElement); } } [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] void ICollection.Add(T item) => Add(item); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); // We use Count() and not Any() because we must ensure full loop pass internal bool AddRange(IEnumerable items) => items.Count(Add) > 0; // We use Count() and not Any() because we must ensure full loop pass internal bool RemoveRange(IEnumerable items) => items.Count(Remove) > 0; internal bool ReplaceIfNeededWith(IReadOnlyCollection other) { if (SetEquals(other)) { return false; } ReplaceWith(other); return true; } internal void ReplaceWith(IEnumerable other) { BackingCollection.Clear(); foreach (T item in other) { BackingCollection[item] = true; } } } }