File: InternalUtilities\SetWithInsertionOrder.cs
Web Access
Project: src\src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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);
    }
}