Set.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. // Copied from https://github.com/dotnet/corefx/blob/ec2685715b01d12f16b08d0dfa326649b12db8ec/src/System.Linq/src/System/Linq/Set.cs
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Diagnostics.CodeAnalysis;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace System.Linq
  11. {
  12. [ExcludeFromCodeCoverage]
  13. internal sealed class Set<TElement>
  14. {
  15. private readonly IEqualityComparer<TElement> _comparer;
  16. private int[] _buckets;
  17. #if DEBUG
  18. private bool _haveRemoved;
  19. #endif
  20. private Slot[] _slots;
  21. public Set(IEqualityComparer<TElement> comparer)
  22. {
  23. _comparer = comparer ?? EqualityComparer<TElement>.Default;
  24. _buckets = new int[7];
  25. _slots = new Slot[7];
  26. }
  27. internal int Count { get; private set; }
  28. // If value is not in set, add it and return true; otherwise return false
  29. public bool Add(TElement value)
  30. {
  31. #if DEBUG
  32. Debug.Assert(!_haveRemoved, "This class is optimized for never calling Add after Remove. If your changes need to do so, undo that optimization.");
  33. #endif
  34. var hashCode = InternalGetHashCode(value);
  35. for (var i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i]._next)
  36. {
  37. if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
  38. {
  39. return false;
  40. }
  41. }
  42. if (Count == _slots.Length)
  43. {
  44. Resize();
  45. }
  46. var index = Count;
  47. Count++;
  48. var bucket = hashCode % _buckets.Length;
  49. _slots[index]._hashCode = hashCode;
  50. _slots[index]._value = value;
  51. _slots[index]._next = _buckets[bucket] - 1;
  52. _buckets[bucket] = index + 1;
  53. return true;
  54. }
  55. // If value is in set, remove it and return true; otherwise return false
  56. public bool Remove(TElement value)
  57. {
  58. #if DEBUG
  59. _haveRemoved = true;
  60. #endif
  61. var hashCode = InternalGetHashCode(value);
  62. var bucket = hashCode % _buckets.Length;
  63. var last = -1;
  64. for (var i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i]._next)
  65. {
  66. if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
  67. {
  68. if (last < 0)
  69. {
  70. _buckets[bucket] = _slots[i]._next + 1;
  71. }
  72. else
  73. {
  74. _slots[last]._next = _slots[i]._next;
  75. }
  76. _slots[i]._hashCode = -1;
  77. _slots[i]._value = default;
  78. _slots[i]._next = -1;
  79. return true;
  80. }
  81. }
  82. return false;
  83. }
  84. internal int InternalGetHashCode(TElement value)
  85. {
  86. // Handle comparer implementations that throw when passed null
  87. return (value == null) ? 0 : _comparer.GetHashCode(value) & 0x7FFFFFFF;
  88. }
  89. internal TElement[] ToArray()
  90. {
  91. #if DEBUG
  92. Debug.Assert(!_haveRemoved, "Optimized ToArray cannot be called if Remove has been called.");
  93. #endif
  94. var array = new TElement[Count];
  95. for (var i = 0; i != array.Length; ++i)
  96. {
  97. array[i] = _slots[i]._value;
  98. }
  99. return array;
  100. }
  101. internal List<TElement> ToList()
  102. {
  103. #if DEBUG
  104. Debug.Assert(!_haveRemoved, "Optimized ToList cannot be called if Remove has been called.");
  105. #endif
  106. var count = Count;
  107. var list = new List<TElement>(count);
  108. for (var i = 0; i != count; ++i)
  109. {
  110. list.Add(_slots[i]._value);
  111. }
  112. return list;
  113. }
  114. private void Resize()
  115. {
  116. var newSize = checked((Count * 2) + 1);
  117. var newBuckets = new int[newSize];
  118. var newSlots = new Slot[newSize];
  119. Array.Copy(_slots, 0, newSlots, 0, Count);
  120. for (var i = 0; i < Count; i++)
  121. {
  122. var bucket = newSlots[i]._hashCode % newSize;
  123. newSlots[i]._next = newBuckets[bucket] - 1;
  124. newBuckets[bucket] = i + 1;
  125. }
  126. _buckets = newBuckets;
  127. _slots = newSlots;
  128. }
  129. public async Task UnionWithAsync(IAsyncEnumerable<TElement> other, CancellationToken cancellationToken)
  130. {
  131. var enu = other.GetAsyncEnumerator(cancellationToken);
  132. try
  133. {
  134. while (await enu.MoveNextAsync().ConfigureAwait(false))
  135. {
  136. Add(enu.Current);
  137. }
  138. }
  139. finally
  140. {
  141. await enu.DisposeAsync().ConfigureAwait(false);
  142. }
  143. }
  144. internal struct Slot
  145. {
  146. internal int _hashCode;
  147. internal int _next;
  148. internal TElement _value;
  149. }
  150. }
  151. }