File: src\RoslynAnalyzers\Utilities\Compiler\PooledObjects\PooledConcurrentSet.cs
Web Access
Project: src\src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\Core\Microsoft.CodeAnalysis.Analyzers.csproj (Microsoft.CodeAnalysis.Analyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
 
#pragma warning disable CA1000 // Do not declare static members on generic types
 
namespace Analyzer.Utilities.PooledObjects
{
    /// <summary>
    /// <see cref="ConcurrentDictionary{TKey, TValue}"/> that can be recycled via an object pool.
    /// </summary>
#pragma warning disable CA1710 // Identifiers should have correct suffix
    internal sealed class PooledConcurrentSet<T> : ICollection<T>, IDisposable
        where T : notnull
#pragma warning restore CA1710 // Identifiers should have correct suffix
    {
        private readonly PooledConcurrentDictionary<T, byte> _dictionary;
 
        private PooledConcurrentSet(PooledConcurrentDictionary<T, byte> dictionary)
        {
            _dictionary = dictionary;
        }
 
        public void Dispose() => Free(CancellationToken.None);
        public void Free(CancellationToken cancellationToken) => _dictionary.Free(cancellationToken);
 
        public static PooledConcurrentSet<T> GetInstance(IEqualityComparer<T>? comparer = null)
        {
            var dictionary = PooledConcurrentDictionary<T, byte>.GetInstance(comparer);
            return new PooledConcurrentSet<T>(dictionary);
        }
 
        public static PooledConcurrentSet<T> GetInstance(IEnumerable<T> initializer, IEqualityComparer<T>? comparer = null)
        {
            var instance = GetInstance(comparer);
            foreach (var item in initializer)
            {
                instance.Add(item);
            }
 
            return instance;
        }
 
        /// <summary>
        /// Obtain the number of elements in the set.
        /// </summary>
        /// <returns>The number of elements in the set.</returns>
        public int Count => _dictionary.Count;
 
        /// <summary>
        /// Determine whether the set is empty.</summary>
        /// <returns>true if the set is empty; otherwise, false.</returns>
        public bool IsEmpty => _dictionary.IsEmpty;
 
        public bool IsReadOnly => false;
 
        /// <summary>
        /// Attempts to add a <paramref name="value"/> to the set.
        /// </summary>
        /// <param name="value">The value to add.</param>
        /// <returns>true if the value was added to the set. If the value already exists, this method returns false.</returns>
        public bool Add(T value) => _dictionary.TryAdd(value, 0);
 
        /// <summary>
        /// Adds the given <paramref name="values"/> to the set.
        /// </summary>
        public void AddRange(IEnumerable<T>? values)
        {
            if (values != null)
            {
                foreach (var v in values)
                {
                    Add(v);
                }
            }
        }
 
        /// <summary>
        /// Attempts to remove a value from the set.
        /// </summary>
        /// <param name="item">The value to remove.</param>
        /// <returns>true if the value was removed successfully; otherwise false.</returns>
        public bool Remove(T item) => _dictionary.TryRemove(item, out _);
 
        /// <summary>
        /// Clears all the elements from the set.
        /// </summary>
        public void Clear() => _dictionary.Clear();
 
        /// <summary>
        /// Returns true if the given <paramref name="item"/> is present in the set.
        /// </summary>
        public bool Contains(T item) => _dictionary.ContainsKey(item);
 
        public void CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException();
 
        /// <summary>
        /// Obtain an enumerator that iterates through the elements in the set.
        /// </summary>
        /// <returns>An enumerator for the set.</returns>
        public KeyEnumerator GetEnumerator()
        {
            // PERF: Do not use dictionary.Keys here because that creates a snapshot
            // of the collection resulting in a List<T> allocation. Instead, use the
            // KeyValuePair enumerator and pick off the Key part.
            return new KeyEnumerator(_dictionary);
        }
 
        private IEnumerator<T> GetEnumeratorCore()
        {
            // PERF: Do not use dictionary.Keys here because that creates a snapshot
            // of the collection resulting in a List<T> allocation. Instead, use the
            // KeyValuePair enumerator and pick off the Key part.
            foreach (var kvp in _dictionary)
            {
                yield return kvp.Key;
            }
        }
 
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return GetEnumeratorCore();
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumeratorCore();
        }
 
        void ICollection<T>.Add(T item)
        {
            Add(item);
        }
 
#pragma warning disable CA1815 // Override equals and operator equals on value types
        public readonly struct KeyEnumerator
#pragma warning restore CA1815 // Override equals and operator equals on value types
        {
            private readonly IEnumerator<KeyValuePair<T, byte>> _kvpEnumerator;
 
            internal KeyEnumerator(IEnumerable<KeyValuePair<T, byte>> data)
            {
                _kvpEnumerator = data.GetEnumerator();
            }
 
            public T Current => _kvpEnumerator.Current.Key;
 
            public bool MoveNext()
            {
                return _kvpEnumerator.MoveNext();
            }
 
            public void Reset()
            {
                _kvpEnumerator.Reset();
            }
        }
    }
}