<Compile Include="System\Linq\Parallel\Enumerables\QueryAggregationOptions.cs" />
<Compile Include="System\Linq\Parallel\Enumerables\RangeEnumerable.cs" />
<Compile Include="System\Linq\Parallel\Enumerables\RepeatEnumerable.cs" />
+ <Compile Include="System\Linq\Parallel\JaggedArray.cs" />
<Compile Include="System\Linq\Parallel\Merging\ArrayMergeHelper.cs" />
<Compile Include="System\Linq\Parallel\Merging\AsynchronousChannelMergeEnumerator.cs" />
<Compile Include="System\Linq\Parallel\Merging\DefaultMergeHelper.cs" />
<Compile Include="System\Linq\Parallel\Scheduling\Scheduling.cs" />
<Compile Include="System\Linq\Parallel\Scheduling\SpoolingTask.cs" />
<Compile Include="System\Linq\Parallel\Scheduling\SpoolingTaskBase.cs" />
- <Compile Include="System\Linq\Parallel\Helpers.cs" />
<Compile Include="System\Linq\Parallel\Utils\CancellableEnumerable.cs" />
<Compile Include="System\Linq\Parallel\Utils\ExceptionAggregator.cs" />
<Compile Include="System\Linq\Parallel\Utils\ExchangeUtilities.cs" />
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-
-namespace System.Linq.Parallel
-{
- internal class JaggedArray<TElement>
- {
- public static TElement[][] Allocate(int size1, int size2)
- {
- TElement[][] ret = new TElement[size1][];
- for (int i = 0; i < size1; i++)
- ret[i] = new TElement[size2];
-
- return ret;
- }
- }
-
- // Copied from Linq.
- internal class Set<TElement>
- {
- private int[] _buckets;
- private Slot[] _slots;
- private int _count;
- private readonly IEqualityComparer<TElement> _comparer;
-#if DEBUG
- private bool _haveRemoved;
-#endif
-
- private const int InitialSize = 7;
- private const int HashCodeMask = 0x7FFFFFFF;
-
- public Set(IEqualityComparer<TElement>? comparer)
- {
- if (comparer == null) comparer = EqualityComparer<TElement>.Default;
- _comparer = comparer;
- _buckets = new int[InitialSize];
- _slots = new Slot[InitialSize];
- }
-
- // If value is not in set, add it and return true; otherwise return false
- public bool Add(TElement value)
- {
-#if DEBUG
- Debug.Assert(!_haveRemoved, "This class is optimised for never calling Add after Remove. If your changes need to do so, undo that optimization.");
-#endif
- return !Find(value, true);
- }
-
- // Check whether value is in set
- public bool Contains(TElement value)
- {
- return Find(value, false);
- }
-
- // If value is in set, remove it and return true; otherwise return false
- public bool Remove(TElement value)
- {
-#if DEBUG
- _haveRemoved = true;
-#endif
- int hashCode = InternalGetHashCode(value);
- int bucket = hashCode % _buckets.Length;
- int last = -1;
- for (int i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i].next)
- {
- if (_slots[i].hashCode == hashCode && _comparer.Equals(_slots[i].value, value))
- {
- if (last < 0)
- {
- _buckets[bucket] = _slots[i].next + 1;
- }
- else
- {
- _slots[last].next = _slots[i].next;
- }
- _slots[i].hashCode = -1;
- _slots[i].value = default;
- _slots[i].next = -1;
- return true;
- }
- }
- return false;
- }
-
- private bool Find(TElement value, bool add)
- {
- int hashCode = InternalGetHashCode(value);
- for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].next)
- {
- if (_slots[i].hashCode == hashCode && _comparer.Equals(_slots[i].value, value)) return true;
- }
- if (add)
- {
- if (_count == _slots.Length) Resize();
- int index = _count;
- _count++;
- int bucket = hashCode % _buckets.Length;
- _slots[index].hashCode = hashCode;
- _slots[index].value = value;
- _slots[index].next = _buckets[bucket] - 1;
- _buckets[bucket] = index + 1;
- }
- return false;
- }
-
- private void Resize()
- {
- int newSize = checked(_count * 2 + 1);
- int[] newBuckets = new int[newSize];
- Slot[] newSlots = new Slot[newSize];
- Array.Copy(_slots, newSlots, _count);
- for (int i = 0; i < _count; i++)
- {
- int bucket = newSlots[i].hashCode % newSize;
- newSlots[i].next = newBuckets[bucket] - 1;
- newBuckets[bucket] = i + 1;
- }
- _buckets = newBuckets;
- _slots = newSlots;
- }
-
- internal int InternalGetHashCode(TElement value)
- {
- // Work around comparer implementations that throw when passed null
- return (value == null) ? 0 : _comparer.GetHashCode(value) & HashCodeMask;
- }
-
- internal struct Slot
- {
- internal int hashCode;
- internal int next;
- internal TElement? value;
- }
- }
-}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Linq.Parallel
+{
+ internal class JaggedArray<TElement>
+ {
+ public static TElement[][] Allocate(int size1, int size2)
+ {
+ TElement[][] ret = new TElement[size1][];
+ for (int i = 0; i < size1; i++)
+ ret[i] = new TElement[size2];
+
+ return ret;
+ }
+ }
+}
private readonly QueryOperatorEnumerator<Pair<TInputOutput, NoKeyMemoizationRequired>, TLeftKey> _leftSource; // Left data source.
private readonly QueryOperatorEnumerator<Pair<TInputOutput, NoKeyMemoizationRequired>, int> _rightSource; // Right data source.
private readonly IEqualityComparer<TInputOutput>? _comparer; // A comparer used for equality checks/hash-coding.
- private Set<TInputOutput>? _hashLookup; // The hash lookup, used to produce the distinct set.
+ private HashSet<TInputOutput>? _hashLookup; // The hash lookup, used to produce the distinct set.
private readonly CancellationToken _cancellationToken;
private Shared<int>? _outputLoopCount;
{
_outputLoopCount = new Shared<int>(0);
- _hashLookup = new Set<TInputOutput>(_comparer);
+ _hashLookup = new HashSet<TInputOutput>(_comparer);
Pair<TInputOutput, NoKeyMemoizationRequired> rightElement = default(Pair<TInputOutput, NoKeyMemoizationRequired>);
int rightKeyUnused = default(int);
// Build the set out of the left data source, if we haven't already.
if (_outputEnumerator == null)
{
- Set<TInputOutput> rightLookup = new Set<TInputOutput>(_comparer);
+ HashSet<TInputOutput> rightLookup = new HashSet<TInputOutput>(_comparer);
Pair<TInputOutput, NoKeyMemoizationRequired> rightElement = default(Pair<TInputOutput, NoKeyMemoizationRequired>);
int rightKeyUnused = default(int);
private readonly QueryOperatorEnumerator<Pair<TInputOutput, NoKeyMemoizationRequired>, TLeftKey> _leftSource; // Left data source.
private readonly QueryOperatorEnumerator<Pair<TInputOutput, NoKeyMemoizationRequired>, int> _rightSource; // Right data source.
private readonly IEqualityComparer<TInputOutput>? _comparer; // Comparer to use for equality/hash-coding.
- private Set<TInputOutput>? _hashLookup; // The hash lookup, used to produce the intersection.
+ private HashSet<TInputOutput>? _hashLookup; // The hash lookup, used to produce the intersection.
private readonly CancellationToken _cancellationToken;
private Shared<int>? _outputLoopCount;
if (_hashLookup == null)
{
_outputLoopCount = new Shared<int>(0);
- _hashLookup = new Set<TInputOutput>(_comparer);
+ _hashLookup = new HashSet<TInputOutput>(_comparer);
Pair<TInputOutput, NoKeyMemoizationRequired> rightElement = default(Pair<TInputOutput, NoKeyMemoizationRequired>);
int rightKeyUnused = default(int);
{
private QueryOperatorEnumerator<Pair<TInputOutput, NoKeyMemoizationRequired>, TLeftKey>? _leftSource; // Left data source.
private QueryOperatorEnumerator<Pair<TInputOutput, NoKeyMemoizationRequired>, TRightKey>? _rightSource; // Right data source.
- private Set<TInputOutput>? _hashLookup; // The hash lookup, used to produce the union.
+ private HashSet<TInputOutput>? _hashLookup; // The hash lookup, used to produce the union.
private readonly CancellationToken _cancellationToken;
private Shared<int>? _outputLoopCount;
private readonly IEqualityComparer<TInputOutput>? _comparer;
{
if (_hashLookup == null)
{
- _hashLookup = new Set<TInputOutput>(_comparer);
+ _hashLookup = new HashSet<TInputOutput>(_comparer);
_outputLoopCount = new Shared<int>(0);
}
private class DistinctQueryOperatorEnumerator<TKey> : QueryOperatorEnumerator<TInputOutput, int>
{
private readonly QueryOperatorEnumerator<Pair<TInputOutput, NoKeyMemoizationRequired>, TKey> _source; // The data source.
- private readonly Set<TInputOutput> _hashLookup; // The hash lookup, used to produce the distinct set.
+ private readonly HashSet<TInputOutput> _hashLookup; // The hash lookup, used to produce the distinct set.
private readonly CancellationToken _cancellationToken;
private Shared<int>? _outputLoopCount; // Allocated in MoveNext to avoid false sharing.
{
Debug.Assert(source != null);
_source = source;
- _hashLookup = new Set<TInputOutput>(comparer);
+ _hashLookup = new HashSet<TInputOutput>(comparer);
_cancellationToken = cancellationToken;
}
<Compile Include="System\Linq\Select.cs" />
<Compile Include="System\Linq\SelectMany.cs" />
<Compile Include="System\Linq\SequenceEqual.cs" />
- <Compile Include="System\Linq\Set.cs" />
<Compile Include="System\Linq\Single.cs" />
<Compile Include="System\Linq\SingleLinkedNode.cs" />
<Compile Include="System\Linq\Skip.cs" />
{
private sealed partial class DistinctIterator<TSource> : IIListProvider<TSource>
{
- private Set<TSource> FillSet()
- {
- var set = new Set<TSource>(_comparer);
- set.UnionWith(_source);
- return set;
- }
+ public TSource[] ToArray() => Enumerable.HashSetToArray(new HashSet<TSource>(_source, _comparer));
- public TSource[] ToArray() => FillSet().ToArray();
+ public List<TSource> ToList() => Enumerable.HashSetToList(new HashSet<TSource>(_source, _comparer));
- public List<TSource> ToList() => FillSet().ToList();
-
- public int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : FillSet().Count;
+ public int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : new HashSet<TSource>(_source, _comparer).Count;
}
}
}
{
private readonly IEnumerable<TSource> _source;
private readonly IEqualityComparer<TSource>? _comparer;
- private Set<TSource>? _set;
+ private HashSet<TSource>? _set;
private IEnumerator<TSource>? _enumerator;
public DistinctIterator(IEnumerable<TSource> source, IEqualityComparer<TSource>? comparer)
}
TSource element = _enumerator.Current;
- _set = new Set<TSource>(_comparer);
+ _set = new HashSet<TSource>(DefaultInternalSetCapacity, _comparer);
_set.Add(element);
_current = element;
_state = 2;
private static IEnumerable<TSource> ExceptIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
{
- Set<TSource> set = new Set<TSource>(comparer);
- set.UnionWith(second);
+ var set = new HashSet<TSource>(second, comparer);
foreach (TSource element in first)
{
private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
{
- Set<TSource> set = new Set<TSource>(comparer);
- set.UnionWith(second);
+ var set = new HashSet<TSource>(second, comparer);
foreach (TSource element in first)
{
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-
-namespace System.Linq
-{
- /// <summary>
- /// A lightweight hash set.
- /// </summary>
- /// <typeparam name="TElement">The type of the set's items.</typeparam>
- internal sealed class Set<TElement>
- {
- /// <summary>
- /// The comparer used to hash and compare items in the set.
- /// </summary>
- private readonly IEqualityComparer<TElement> _comparer;
-
- /// <summary>
- /// The hash buckets, which are used to index into the slots.
- /// </summary>
- private int[] _buckets;
-
- /// <summary>
- /// The slots, each of which store an item and its hash code.
- /// </summary>
- private Slot[] _slots;
-
- /// <summary>
- /// The number of items in this set.
- /// </summary>
- private int _count;
-
-#if DEBUG
- /// <summary>
- /// Whether <see cref="Remove"/> has been called on this set.
- /// </summary>
- /// <remarks>
- /// When <see cref="Remove"/> runs in debug builds, this flag is set to <c>true</c>.
- /// Other methods assert that this flag is <c>false</c> in debug builds, because
- /// they make optimizations that may not be correct if <see cref="Remove"/> is called
- /// beforehand.
- /// </remarks>
- private bool _haveRemoved;
-#endif
-
- /// <summary>
- /// Constructs a set that compares items with the specified comparer.
- /// </summary>
- /// <param name="comparer">
- /// The comparer. If this is <c>null</c>, it defaults to <see cref="EqualityComparer{TElement}.Default"/>.
- /// </param>
- public Set(IEqualityComparer<TElement>? comparer)
- {
- _comparer = comparer ?? EqualityComparer<TElement>.Default;
- _buckets = new int[7];
- _slots = new Slot[7];
- }
-
- /// <summary>
- /// Attempts to add an item to this set.
- /// </summary>
- /// <param name="value">The item to add.</param>
- /// <returns>
- /// <c>true</c> if the item was not in the set; otherwise, <c>false</c>.
- /// </returns>
- public bool Add(TElement value)
- {
-#if DEBUG
- Debug.Assert(!_haveRemoved, "This class is optimised for never calling Add after Remove. If your changes need to do so, undo that optimization.");
-#endif
- int hashCode = InternalGetHashCode(value);
- for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i]._next)
- {
- if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
- {
- return false;
- }
- }
-
- if (_count == _slots.Length)
- {
- Resize();
- }
-
- int index = _count;
- _count++;
- int bucket = hashCode % _buckets.Length;
- _slots[index]._hashCode = hashCode;
- _slots[index]._value = value;
- _slots[index]._next = _buckets[bucket] - 1;
- _buckets[bucket] = index + 1;
- return true;
- }
-
- /// <summary>
- /// Attempts to remove an item from this set.
- /// </summary>
- /// <param name="value">The item to remove.</param>
- /// <returns>
- /// <c>true</c> if the item was in the set; otherwise, <c>false</c>.
- /// </returns>
- public bool Remove(TElement value)
- {
-#if DEBUG
- _haveRemoved = true;
-#endif
- int hashCode = InternalGetHashCode(value);
- int bucket = hashCode % _buckets.Length;
- int last = -1;
- for (int i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i]._next)
- {
- if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
- {
- if (last < 0)
- {
- _buckets[bucket] = _slots[i]._next + 1;
- }
- else
- {
- _slots[last]._next = _slots[i]._next;
- }
-
- _slots[i]._hashCode = -1;
- _slots[i]._value = default!;
- _slots[i]._next = -1;
- return true;
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Expands the capacity of this set to double the current capacity, plus one.
- /// </summary>
- private void Resize()
- {
- int newSize = checked((_count * 2) + 1);
- int[] newBuckets = new int[newSize];
- Slot[] newSlots = new Slot[newSize];
- Array.Copy(_slots, newSlots, _count);
- for (int i = 0; i < _count; i++)
- {
- int bucket = newSlots[i]._hashCode % newSize;
- newSlots[i]._next = newBuckets[bucket] - 1;
- newBuckets[bucket] = i + 1;
- }
-
- _buckets = newBuckets;
- _slots = newSlots;
- }
-
- /// <summary>
- /// Creates an array from the items in this set.
- /// </summary>
- /// <returns>An array of the items in this set.</returns>
- public TElement[] ToArray()
- {
-#if DEBUG
- Debug.Assert(!_haveRemoved, "Optimised ToArray cannot be called if Remove has been called.");
-#endif
- TElement[] array = new TElement[_count];
- for (int i = 0; i != array.Length; ++i)
- {
- array[i] = _slots[i]._value;
- }
-
- return array;
- }
-
- /// <summary>
- /// Creates a list from the items in this set.
- /// </summary>
- /// <returns>A list of the items in this set.</returns>
- public List<TElement> ToList()
- {
-#if DEBUG
- Debug.Assert(!_haveRemoved, "Optimised ToList cannot be called if Remove has been called.");
-#endif
- int count = _count;
- List<TElement> list = new List<TElement>(count);
- for (int i = 0; i != count; ++i)
- {
- list.Add(_slots[i]._value);
- }
-
- return list;
- }
-
- /// <summary>
- /// The number of items in this set.
- /// </summary>
- public int Count => _count;
-
- /// <summary>
- /// Unions this set with an enumerable.
- /// </summary>
- /// <param name="other">The enumerable.</param>
- public void UnionWith(IEnumerable<TElement> other)
- {
- Debug.Assert(other != null);
-
- foreach (TElement item in other)
- {
- Add(item);
- }
- }
-
- /// <summary>
- /// Gets the hash code of the provided value with its sign bit zeroed out, so that modulo has a positive result.
- /// </summary>
- /// <param name="value">The value to hash.</param>
- /// <returns>The lower 31 bits of the value's hash code.</returns>
- private int InternalGetHashCode(TElement value) => value == null ? 0 : _comparer.GetHashCode(value) & 0x7FFFFFFF;
-
- /// <summary>
- /// An entry in the hash set.
- /// </summary>
- private struct Slot
- {
- /// <summary>
- /// The hash code of the item.
- /// </summary>
- internal int _hashCode;
-
- /// <summary>
- /// In the case of a hash collision, the index of the next slot to probe.
- /// </summary>
- internal int _next;
-
- /// <summary>
- /// The item held by this slot.
- /// </summary>
- internal TElement _value;
- }
- }
-}
// Don't pre-allocate based on knowledge of size, as potentially many elements will be dropped.
return new HashSet<TSource>(source, comparer);
}
+
+ /// <summary>Default initial capacity to use when creating sets for internal temporary storage.</summary>
+ /// <remarks>This is based on the implicit size used in previous implementations, which used a custom Set type.</remarks>
+ private const int DefaultInternalSetCapacity = 7;
+
+ private static TSource[] HashSetToArray<TSource>(HashSet<TSource> set)
+ {
+ var result = new TSource[set.Count];
+ set.CopyTo(result);
+ return result;
+ }
+
+ private static List<TSource> HashSetToList<TSource>(HashSet<TSource> set)
+ {
+ var result = new List<TSource>(set.Count);
+
+ foreach (TSource item in set)
+ {
+ result.Add(item);
+ }
+
+ return result;
+ }
}
}
{
private abstract partial class UnionIterator<TSource> : IIListProvider<TSource>
{
- private Set<TSource> FillSet()
+ private HashSet<TSource> FillSet()
{
- var set = new Set<TSource>(_comparer);
+ var set = new HashSet<TSource>(_comparer);
for (int index = 0; ; ++index)
{
IEnumerable<TSource>? enumerable = GetEnumerable(index);
}
}
- public TSource[] ToArray() => FillSet().ToArray();
+ public TSource[] ToArray() => Enumerable.HashSetToArray(FillSet());
- public List<TSource> ToList() => FillSet().ToList();
+ public List<TSource> ToList() => Enumerable.HashSetToList(FillSet());
public int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : FillSet().Count;
}
{
internal readonly IEqualityComparer<TSource>? _comparer;
private IEnumerator<TSource>? _enumerator;
- private Set<TSource>? _set;
+ private HashSet<TSource>? _set;
protected UnionIterator(IEqualityComparer<TSource>? comparer)
{
{
Debug.Assert(_enumerator != null);
- Set<TSource> set = new Set<TSource>(_comparer);
+ var set = new HashSet<TSource>(DefaultInternalSetCapacity, _comparer);
TSource element = _enumerator.Current;
set.Add(element);
_current = element;
Debug.Assert(_enumerator != null);
Debug.Assert(_set != null);
- Set<TSource> set = _set;
+ HashSet<TSource> set = _set;
while (_enumerator.MoveNext())
{