File: src\Compilers\Core\Portable\InternalUtilities\SetWithInsertionOrder.cs
Web Access
Project: src\src\Workspaces\Core\MSBuild.BuildHost\Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj (Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost)
// 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.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Roslyn.Utilities
{
    /// <summary>
    /// A set that returns the inserted values in insertion order.
    /// The mutation operations are not thread-safe.
    /// </summary>
    internal sealed class SetWithInsertionOrder<T> : IOrderedReadOnlySet<T>
    {
        private HashSet<T>? _set = null;
        private ArrayBuilder<T>? _elements = null;
 
        public bool Add(T value)
        {
            if (_set == null)
            {
                _set = new HashSet<T>();
                _elements = new ArrayBuilder<T>();
            }
 
            if (!_set.Add(value))
            {
                return false;
            }
 
            _elements!.Add(value);
            return true;
        }
 
        public bool Insert(int index, T value)
        {
            if (_set == null)
            {
                if (index > 0)
                {
                    throw new IndexOutOfRangeException();
                }
                Add(value);
            }
            else
            {
                if (!_set.Add(value))
                {
                    return false;
                }
 
                try
                {
                    _elements!.Insert(index, value);
                }
                catch
                {
                    _set.Remove(value);
                    throw;
                }
            }
            return true;
        }
 
        public bool Remove(T value)
        {
            if (_set is null)
            {
                return false;
            }
 
            if (!_set.Remove(value))
            {
                return false;
            }
            _elements!.RemoveAt(_elements.IndexOf(value));
            return true;
        }
 
        public int Count => _elements?.Count ?? 0;
 
        public bool Contains(T value) => _set?.Contains(value) ?? false;
 
        public IEnumerator<T> GetEnumerator()
            => _elements is null ? SpecializedCollections.EmptyEnumerator<T>() : ((IEnumerable<T>)_elements).GetEnumerator();
 
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
        public ImmutableArray<T> AsImmutable() => _elements.ToImmutableArrayOrEmpty();
 
        public T this[int i] => _elements![i];
 
        private IReadOnlySet<T> Set
            => (IReadOnlySet<T>?)_set ?? SpecializedCollections.EmptyReadOnlySet<T>();
 
        public bool IsProperSubsetOf(IEnumerable<T> other)
            => Set.IsProperSubsetOf(other);
 
        public bool IsProperSupersetOf(IEnumerable<T> other)
            => Set.IsProperSupersetOf(other);
 
        public bool IsSubsetOf(IEnumerable<T> other)
            => Set.IsSubsetOf(other);
 
        public bool IsSupersetOf(IEnumerable<T> other)
            => Set.IsSupersetOf(other);
 
        public bool Overlaps(IEnumerable<T> other)
            => Set.Overlaps(other);
 
        public bool SetEquals(IEnumerable<T> other)
            => Set.SetEquals(other);
    }
}