Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Implement ImmutableSegmentedHashSet<T> #54719

Merged
merged 9 commits into from
Jun 28, 2022
52 changes: 39 additions & 13 deletions src/Dependencies/Collections/ImmutableSegmentedHashSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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.Generic;
using System.Collections.Immutable;

Expand All @@ -11,54 +12,79 @@ internal static class ImmutableSegmentedHashSet
{
/// <inheritdoc cref="ImmutableHashSet.Create{T}()"/>
public static ImmutableSegmentedHashSet<T> Create<T>()
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty;

/// <inheritdoc cref="ImmutableHashSet.Create{T}(T)"/>
public static ImmutableSegmentedHashSet<T> Create<T>(T item)
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty.Add(item);

/// <inheritdoc cref="ImmutableHashSet.Create{T}(T[])"/>
public static ImmutableSegmentedHashSet<T> Create<T>(params T[] items)
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty.Union(items);

/// <inheritdoc cref="ImmutableHashSet.Create{T}(IEqualityComparer{T})"/>
public static ImmutableSegmentedHashSet<T> Create<T>(IEqualityComparer<T>? equalityComparer)
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty.WithComparer(equalityComparer);

/// <inheritdoc cref="ImmutableHashSet.Create{T}(IEqualityComparer{T}?, T)"/>
public static ImmutableSegmentedHashSet<T> Create<T>(IEqualityComparer<T>? equalityComparer, T item)
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty.WithComparer(equalityComparer).Add(item);

/// <inheritdoc cref="ImmutableHashSet.Create{T}(IEqualityComparer{T}?, T[])"/>
public static ImmutableSegmentedHashSet<T> Create<T>(IEqualityComparer<T>? equalityComparer, params T[] items)
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty.WithComparer(equalityComparer).Union(items);

/// <inheritdoc cref="ImmutableHashSet.CreateBuilder{T}()"/>
public static ImmutableSegmentedHashSet<T>.Builder CreateBuilder<T>()
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty.ToBuilder();

/// <inheritdoc cref="ImmutableHashSet.CreateBuilder{T}(IEqualityComparer{T}?)"/>
public static ImmutableSegmentedHashSet<T>.Builder CreateBuilder<T>(IEqualityComparer<T>? equalityComparer)
=> throw null!;
=> ImmutableSegmentedHashSet<T>.Empty.WithComparer(equalityComparer).ToBuilder();

/// <inheritdoc cref="ImmutableHashSet.CreateRange{T}(IEnumerable{T})"/>
public static ImmutableSegmentedHashSet<T> CreateRange<T>(IEnumerable<T> items)
=> throw null!;
{
if (items is ImmutableSegmentedHashSet<T> existingSet)
return existingSet.WithComparer(null);

return ImmutableSegmentedHashSet<T>.Empty.Union(items);
}

/// <inheritdoc cref="ImmutableHashSet.CreateRange{T}(IEqualityComparer{T}?, IEnumerable{T})"/>
public static ImmutableSegmentedHashSet<T> CreateRange<T>(IEqualityComparer<T>? equalityComparer, IEnumerable<T> items)
=> throw null!;
{
if (items is ImmutableSegmentedHashSet<T> existingSet)
return existingSet.WithComparer(equalityComparer);

return ImmutableSegmentedHashSet<T>.Empty.WithComparer(equalityComparer).Union(items);
}

/// <inheritdoc cref="ImmutableHashSet.ToImmutableHashSet{TSource}(IEnumerable{TSource})"/>
public static ImmutableSegmentedHashSet<TSource> ToImmutableSegmentedHashSet<TSource>(this IEnumerable<TSource> source)
=> throw null!;
{
if (source is ImmutableSegmentedHashSet<TSource> existingSet)
return existingSet.WithComparer(null);

return ImmutableSegmentedHashSet<TSource>.Empty.Union(source);
}

/// <inheritdoc cref="ImmutableHashSet.ToImmutableHashSet{TSource}(IEnumerable{TSource}, IEqualityComparer{TSource}?)"/>
public static ImmutableSegmentedHashSet<TSource> ToImmutableSegmentedHashSet<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource>? equalityComparer)
=> throw null!;
{
if (source is ImmutableSegmentedHashSet<TSource> existingSet)
return existingSet.WithComparer(equalityComparer);

return ImmutableSegmentedHashSet<TSource>.Empty.WithComparer(equalityComparer).Union(source);
}

/// <inheritdoc cref="ImmutableHashSet.ToImmutableHashSet{TSource}(ImmutableHashSet{TSource}.Builder)"/>
public static ImmutableSegmentedHashSet<TSource> ToImmutableSegmentedHashSet<TSource>(this ImmutableSegmentedHashSet<TSource>.Builder builder)
=> throw null!;
{
if (builder is null)
throw new ArgumentNullException(nameof(builder));

return builder.ToImmutable();
}
}
}
106 changes: 82 additions & 24 deletions src/Dependencies/Collections/ImmutableSegmentedHashSet`1+Builder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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;
Expand All @@ -12,92 +13,149 @@ internal readonly partial struct ImmutableSegmentedHashSet<T>
{
public sealed class Builder : ISet<T>, IReadOnlyCollection<T>
{
/// <summary>
/// The immutable collection this builder is based on.
/// </summary>
private ImmutableSegmentedHashSet<T> _set;

/// <summary>
/// The current mutable collection this builder is operating on. This field is initialized to a copy of
/// <see cref="_set"/> the first time a change is made.
/// </summary>
private SegmentedHashSet<T>? _mutableSet;

internal Builder(ImmutableSegmentedHashSet<T> set)
=> throw null!;
{
_set = set;
_mutableSet = null;
}

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.KeyComparer"/>
public IEqualityComparer<T> KeyComparer => throw null!;
public IEqualityComparer<T> KeyComparer => ReadOnlySet.Comparer;

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.Count"/>
public int Count => throw null!;
public int Count => ReadOnlySet.Count;

private SegmentedHashSet<T> ReadOnlySet => _mutableSet ?? _set._set;

bool ICollection<T>.IsReadOnly => false;

bool ICollection<T>.IsReadOnly => throw null!;
private SegmentedHashSet<T> GetOrCreateMutableSet()
{
if (_mutableSet is null)
{
var originalSet = RoslynImmutableInterlocked.InterlockedExchange(ref _set, default);
if (originalSet.IsDefault)
throw new InvalidOperationException($"Unexpected concurrent access to {GetType()}");

_mutableSet = new SegmentedHashSet<T>(originalSet._set, originalSet.KeyComparer);
}

return _mutableSet;
}

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.Add(T)"/>
public bool Add(T item)
=> throw null!;
{
if (_mutableSet is null && Contains(item))
return false;

return GetOrCreateMutableSet().Add(item);
}

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.Clear()"/>
public void Clear()
=> throw null!;
{
if (ReadOnlySet.Count != 0)
{
if (_mutableSet is null)
{
_mutableSet = new SegmentedHashSet<T>(KeyComparer);
_set = default;
}
else
{
_mutableSet.Clear();
}
}
}

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.Contains(T)"/>
public bool Contains(T item)
=> throw null!;
=> ReadOnlySet.Contains(item);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.ExceptWith(IEnumerable{T})"/>
public void ExceptWith(IEnumerable<T> other)
=> throw null!;
=> GetOrCreateMutableSet().ExceptWith(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.GetEnumerator()"/>
public Enumerator GetEnumerator()
=> throw null!;
=> new Enumerator(GetOrCreateMutableSet());

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.IntersectWith(IEnumerable{T})"/>
public void IntersectWith(IEnumerable<T> other)
=> throw null!;
=> GetOrCreateMutableSet().IntersectWith(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.IsProperSubsetOf(IEnumerable{T})"/>
public bool IsProperSubsetOf(IEnumerable<T> other)
=> throw null!;
=> ReadOnlySet.IsProperSubsetOf(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.IsProperSupersetOf(IEnumerable{T})"/>
public bool IsProperSupersetOf(IEnumerable<T> other)
=> throw null!;
=> ReadOnlySet.IsProperSupersetOf(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.IsSubsetOf(IEnumerable{T})"/>
public bool IsSubsetOf(IEnumerable<T> other)
=> throw null!;
=> ReadOnlySet.IsSubsetOf(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.IsSupersetOf(IEnumerable{T})"/>
public bool IsSupersetOf(IEnumerable<T> other)
=> throw null!;
=> ReadOnlySet.IsSupersetOf(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.Overlaps(IEnumerable{T})"/>
public bool Overlaps(IEnumerable<T> other)
=> throw null!;
=> ReadOnlySet.Overlaps(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.Remove(T)"/>
public bool Remove(T item)
=> throw null!;
{
if (_mutableSet is null && !Contains(item))
return false;

return GetOrCreateMutableSet().Remove(item);
}

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.SetEquals(IEnumerable{T})"/>
public bool SetEquals(IEnumerable<T> other)
=> throw null!;
=> ReadOnlySet.SetEquals(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.SymmetricExceptWith(IEnumerable{T})"/>
public void SymmetricExceptWith(IEnumerable<T> other)
=> throw null!;
=> GetOrCreateMutableSet().SymmetricExceptWith(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.UnionWith(IEnumerable{T})"/>
public void UnionWith(IEnumerable<T> other)
=> throw null!;
=> GetOrCreateMutableSet().UnionWith(other);

/// <inheritdoc cref="ImmutableHashSet{T}.Builder.ToImmutable()"/>
public ImmutableSegmentedHashSet<T> ToImmutable()
=> throw null!;
{
_set = new ImmutableSegmentedHashSet<T>(ReadOnlySet);
_mutableSet = null;
return _set;
}

void ICollection<T>.Add(T item)
=> throw null!;
=> ((ICollection<T>)GetOrCreateMutableSet()).Add(item);

void ICollection<T>.CopyTo(T[] array, int arrayIndex)
=> throw null!;
=> ((ICollection<T>)ReadOnlySet).CopyTo(array, arrayIndex);

IEnumerator<T> IEnumerable<T>.GetEnumerator()
=> throw null!;
=> GetEnumerator();

IEnumerator IEnumerable.GetEnumerator()
=> throw null!;
=> GetEnumerator();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,35 @@ internal readonly partial struct ImmutableSegmentedHashSet<T>
/// <inheritdoc cref="ImmutableHashSet{T}.Enumerator"/>
public struct Enumerator : IEnumerator<T>
{
private readonly SegmentedHashSet<T> _set;
private SegmentedHashSet<T>.Enumerator _enumerator;

internal Enumerator(SegmentedHashSet<T> set)
=> throw null!;
{
_set = set;
_enumerator = set.GetEnumerator();
}

/// <inheritdoc cref="ImmutableHashSet{T}.Enumerator.Current"/>
public T Current => throw null!;
public T Current => _enumerator.Current;

object? IEnumerator.Current => throw null!;
object? IEnumerator.Current => ((IEnumerator)_enumerator).Current;

/// <inheritdoc cref="ImmutableHashSet{T}.Enumerator.Dispose()"/>
public void Dispose()
=> throw null!;
=> _enumerator.Dispose();

/// <inheritdoc cref="ImmutableHashSet{T}.Enumerator.MoveNext()"/>
public bool MoveNext()
=> throw null!;
=> _enumerator.MoveNext();

/// <inheritdoc cref="ImmutableHashSet{T}.Enumerator.Reset()"/>
public void Reset()
=> throw null!;
{
// Create a new enumerator, since _enumerator.Reset() will fail for cases where the set was mutated
// after enumeration started, and ImmutableSegmentHashSet<T>.Builder allows for this case without error.
_enumerator = _set.GetEnumerator();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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.Runtime.CompilerServices;
using System.Threading;

namespace Microsoft.CodeAnalysis.Collections
{
internal readonly partial struct ImmutableSegmentedHashSet<T>
{
/// <summary>
/// Private helper class for use only by <see cref="RoslynImmutableInterlocked"/>.
/// </summary>
internal static class PrivateInterlocked
{
internal static ImmutableSegmentedHashSet<T> VolatileRead(in ImmutableSegmentedHashSet<T> location)
{
var set = Volatile.Read(ref Unsafe.AsRef(in location._set));
if (set is null)
return default;

return new ImmutableSegmentedHashSet<T>(set);
}

internal static ImmutableSegmentedHashSet<T> InterlockedExchange(ref ImmutableSegmentedHashSet<T> location, ImmutableSegmentedHashSet<T> value)
{
var set = Interlocked.Exchange(ref Unsafe.AsRef(in location._set), value._set);
if (set is null)
return default;

return new ImmutableSegmentedHashSet<T>(set);
}

internal static ImmutableSegmentedHashSet<T> InterlockedCompareExchange(ref ImmutableSegmentedHashSet<T> location, ImmutableSegmentedHashSet<T> value, ImmutableSegmentedHashSet<T> comparand)
{
var set = Interlocked.CompareExchange(ref Unsafe.AsRef(in location._set), value._set, comparand._set);
if (set is null)
return default;

return new ImmutableSegmentedHashSet<T>(set);
}
}
}
}
Loading