Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 15 additions & 19 deletions src/System.Linq.Parallel/src/System/Linq/Parallel/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics;

namespace System.Linq.Parallel
{
Expand All @@ -24,26 +25,28 @@ internal class Set<TElement>
private int[] _buckets;
private Slot[] _slots;
private int _count;
private int _freeList;
private IEqualityComparer<TElement> _comparer;
private readonly IEqualityComparer<TElement> _comparer;
#if DEBUG
private bool _haveRemoved;
#endif

private const int InitialSize = 7;
private const int HashCodeMask = 0x7FFFFFFF;

public Set() : this(null) { }

public Set(IEqualityComparer<TElement> comparer)
{
if (comparer == null) comparer = EqualityComparer<TElement>.Default;
_comparer = comparer;
_buckets = new int[InitialSize];
_slots = new Slot[InitialSize];
_freeList = -1;
}

// 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);
}

Expand All @@ -56,6 +59,9 @@ public bool Contains(TElement value)
// 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;
Expand All @@ -73,8 +79,7 @@ public bool Remove(TElement value)
}
_slots[i].hashCode = -1;
_slots[i].value = default(TElement);
_slots[i].next = _freeList;
_freeList = i;
_slots[i].next = -1;
return true;
}
}
Expand All @@ -90,18 +95,9 @@ bool Find(TElement value, bool add)
}
if (add)
{
int index;
if (_freeList >= 0)
{
index = _freeList;
_freeList = _slots[index].next;
}
else
{
if (_count == _slots.Length) Resize();
index = _count;
_count++;
}
if (_count == _slots.Length) Resize();
int index = _count;
_count++;
int bucket = hashCode % _buckets.Length;
_slots[index].hashCode = hashCode;
_slots[index].value = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,8 @@ internal override bool MoveNext(ref TInputOutput currentElement, ref int current
// If we found the element in our set, and if we haven't returned it yet,
// we can yield it to the caller. We also mark it so we know we've returned
// it once already and never will again.
if (_hashLookup.Contains((TInputOutput)leftElement.First))
if (_hashLookup.Remove((TInputOutput)leftElement.First))
{
_hashLookup.Remove((TInputOutput)leftElement.First);
currentElement = (TInputOutput)leftElement.First;
#if DEBUG
currentKey = unchecked((int)0xdeadbeef);
Expand Down
61 changes: 25 additions & 36 deletions src/System.Linq/src/System/Linq/Enumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3667,27 +3667,47 @@ internal class Set<TElement>
private int[] _buckets;
private Slot[] _slots;
private int _count;
private int _freeList;
private IEqualityComparer<TElement> _comparer;
private readonly IEqualityComparer<TElement> _comparer;
#if DEBUG
private bool _haveRemoved;
#endif

public Set(IEqualityComparer<TElement> comparer)
{
if (comparer == null) comparer = EqualityComparer<TElement>.Default;
_comparer = comparer;
_buckets = new int[7];
_slots = new Slot[7];
_freeList = -1;
}

// If value is not in set, add it and return true; otherwise return false
public bool Add(TElement value)
{
return !Find(value, true);
#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;
}

// 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;
Expand All @@ -3705,44 +3725,13 @@ public bool Remove(TElement value)
}
_slots[i].hashCode = -1;
_slots[i].value = default(TElement);
_slots[i].next = _freeList;
_freeList = i;
_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)
{
int index;
if (_freeList >= 0)
{
index = _freeList;
_freeList = _slots[index].next;
}
else
{
if (_count == _slots.Length) Resize();
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);
Expand Down