PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.Core/System.Collections.Generic/HashSet.cs

https://bitbucket.org/steenlund/mono-2.6.7-for-amiga
C# | 720 lines | 510 code | 148 blank | 62 comment | 125 complexity | bea51d951717937a4fa8210a25903b1a MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, LGPL-2.1
  1. //
  2. // HashSet.cs
  3. //
  4. // Authors:
  5. // Jb Evain <jbevain@novell.com>
  6. //
  7. // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Collections;
  30. using System.Collections.Generic;
  31. using System.Linq;
  32. using System.Runtime.Serialization;
  33. using System.Runtime.InteropServices;
  34. using System.Security;
  35. using System.Security.Permissions;
  36. // HashSet is basically implemented as a reduction of Dictionary<K, V>
  37. namespace System.Collections.Generic {
  38. [Serializable, HostProtection (SecurityAction.LinkDemand, MayLeakOnAbort = true)]
  39. public class HashSet<T> : ICollection<T>, ISerializable, IDeserializationCallback {
  40. const int INITIAL_SIZE = 10;
  41. const float DEFAULT_LOAD_FACTOR = (90f / 100);
  42. const int NO_SLOT = -1;
  43. const int HASH_FLAG = -2147483648;
  44. struct Link {
  45. public int HashCode;
  46. public int Next;
  47. }
  48. // The hash table contains indices into the "links" array
  49. int [] table;
  50. Link [] links;
  51. T [] slots;
  52. // The number of slots in "links" and "slots" that
  53. // are in use (i.e. filled with data) or have been used and marked as
  54. // "empty" later on.
  55. int touched;
  56. // The index of the first slot in the "empty slots chain".
  57. // "Remove ()" prepends the cleared slots to the empty chain.
  58. // "Add ()" fills the first slot in the empty slots chain with the
  59. // added item (or increases "touched" if the chain itself is empty).
  60. int empty_slot;
  61. // The number of items in this set.
  62. int count;
  63. // The number of items the set can hold without
  64. // resizing the hash table and the slots arrays.
  65. int threshold;
  66. IEqualityComparer<T> comparer;
  67. SerializationInfo si;
  68. // The number of changes made to this set. Used by enumerators
  69. // to detect changes and invalidate themselves.
  70. int generation;
  71. public int Count {
  72. get { return count; }
  73. }
  74. public HashSet ()
  75. {
  76. Init (INITIAL_SIZE, null);
  77. }
  78. public HashSet (IEqualityComparer<T> comparer)
  79. {
  80. Init (INITIAL_SIZE, comparer);
  81. }
  82. public HashSet (IEnumerable<T> collection) : this (collection, null)
  83. {
  84. }
  85. public HashSet (IEnumerable<T> collection, IEqualityComparer<T> comparer)
  86. {
  87. if (collection == null)
  88. throw new ArgumentNullException ("collection");
  89. int capacity = 0;
  90. var col = collection as ICollection<T>;
  91. if (col != null)
  92. capacity = col.Count;
  93. Init (capacity, comparer);
  94. foreach (var item in collection)
  95. Add (item);
  96. }
  97. protected HashSet (SerializationInfo info, StreamingContext context)
  98. {
  99. si = info;
  100. }
  101. void Init (int capacity, IEqualityComparer<T> comparer)
  102. {
  103. if (capacity < 0)
  104. throw new ArgumentOutOfRangeException ("capacity");
  105. this.comparer = comparer ?? EqualityComparer<T>.Default;
  106. if (capacity == 0)
  107. capacity = INITIAL_SIZE;
  108. /* Modify capacity so 'capacity' elements can be added without resizing */
  109. capacity = (int) (capacity / DEFAULT_LOAD_FACTOR) + 1;
  110. InitArrays (capacity);
  111. generation = 0;
  112. }
  113. void InitArrays (int size)
  114. {
  115. table = new int [size];
  116. links = new Link [size];
  117. empty_slot = NO_SLOT;
  118. slots = new T [size];
  119. touched = 0;
  120. threshold = (int) (table.Length * DEFAULT_LOAD_FACTOR);
  121. if (threshold == 0 && table.Length > 0)
  122. threshold = 1;
  123. }
  124. bool SlotsContainsAt (int index, int hash, T item)
  125. {
  126. int current = table [index] - 1;
  127. while (current != NO_SLOT) {
  128. Link link = links [current];
  129. if (link.HashCode == hash && ((hash == HASH_FLAG && (item == null || null == slots [current])) ? (item == null && null == slots [current]) : comparer.Equals (item, slots [current])))
  130. return true;
  131. current = link.Next;
  132. }
  133. return false;
  134. }
  135. public void CopyTo (T [] array)
  136. {
  137. CopyTo (array, 0, count);
  138. }
  139. public void CopyTo (T [] array, int index)
  140. {
  141. CopyTo (array, index, count);
  142. }
  143. public void CopyTo (T [] array, int index, int count)
  144. {
  145. if (array == null)
  146. throw new ArgumentNullException ("array");
  147. if (index < 0)
  148. throw new ArgumentOutOfRangeException ("index");
  149. if (index > array.Length)
  150. throw new ArgumentException ("index larger than largest valid index of array");
  151. if (array.Length - index < count)
  152. throw new ArgumentException ("Destination array cannot hold the requested elements!");
  153. for (int i = 0, items = 0; i < touched && items < count; i++) {
  154. if (GetLinkHashCode (i) != 0)
  155. array [index++] = slots [i];
  156. }
  157. }
  158. void Resize ()
  159. {
  160. int newSize = PrimeHelper.ToPrime ((table.Length << 1) | 1);
  161. // allocate new hash table and link slots array
  162. var newTable = new int [newSize];
  163. var newLinks = new Link [newSize];
  164. for (int i = 0; i < table.Length; i++) {
  165. int current = table [i] - 1;
  166. while (current != NO_SLOT) {
  167. int hashCode = newLinks [current].HashCode = GetItemHashCode (slots [current]);
  168. int index = (hashCode & int.MaxValue) % newSize;
  169. newLinks [current].Next = newTable [index] - 1;
  170. newTable [index] = current + 1;
  171. current = links [current].Next;
  172. }
  173. }
  174. table = newTable;
  175. links = newLinks;
  176. // allocate new data slots, copy data
  177. var newSlots = new T [newSize];
  178. Array.Copy (slots, 0, newSlots, 0, touched);
  179. slots = newSlots;
  180. threshold = (int) (newSize * DEFAULT_LOAD_FACTOR);
  181. }
  182. int GetLinkHashCode (int index)
  183. {
  184. return links [index].HashCode & HASH_FLAG;
  185. }
  186. int GetItemHashCode (T item)
  187. {
  188. if (item == null)
  189. return HASH_FLAG;
  190. return comparer.GetHashCode (item) | HASH_FLAG;
  191. }
  192. public bool Add (T item)
  193. {
  194. int hashCode = GetItemHashCode (item);
  195. int index = (hashCode & int.MaxValue) % table.Length;
  196. if (SlotsContainsAt (index, hashCode, item))
  197. return false;
  198. if (++count > threshold) {
  199. Resize ();
  200. index = (hashCode & int.MaxValue) % table.Length;
  201. }
  202. // find an empty slot
  203. int current = empty_slot;
  204. if (current == NO_SLOT)
  205. current = touched++;
  206. else
  207. empty_slot = links [current].Next;
  208. // store the hash code of the added item,
  209. // prepend the added item to its linked list,
  210. // update the hash table
  211. links [current].HashCode = hashCode;
  212. links [current].Next = table [index] - 1;
  213. table [index] = current + 1;
  214. // store item
  215. slots [current] = item;
  216. generation++;
  217. return true;
  218. }
  219. public IEqualityComparer<T> Comparer {
  220. get { return comparer; }
  221. }
  222. public void Clear ()
  223. {
  224. count = 0;
  225. Array.Clear (table, 0, table.Length);
  226. Array.Clear (slots, 0, slots.Length);
  227. Array.Clear (links, 0, links.Length);
  228. // empty the "empty slots chain"
  229. empty_slot = NO_SLOT;
  230. touched = 0;
  231. generation++;
  232. }
  233. public bool Contains (T item)
  234. {
  235. int hashCode = GetItemHashCode (item);
  236. int index = (hashCode & int.MaxValue) % table.Length;
  237. return SlotsContainsAt (index, hashCode, item);
  238. }
  239. public bool Remove (T item)
  240. {
  241. // get first item of linked list corresponding to given key
  242. int hashCode = GetItemHashCode (item);
  243. int index = (hashCode & int.MaxValue) % table.Length;
  244. int current = table [index] - 1;
  245. // if there is no linked list, return false
  246. if (current == NO_SLOT)
  247. return false;
  248. // walk linked list until right slot (and its predecessor) is
  249. // found or end is reached
  250. int prev = NO_SLOT;
  251. do {
  252. Link link = links [current];
  253. if (link.HashCode == hashCode && ((hashCode == HASH_FLAG && (item == null || null == slots [current])) ? (item == null && null == slots [current]) : comparer.Equals (slots [current], item)))
  254. break;
  255. prev = current;
  256. current = link.Next;
  257. } while (current != NO_SLOT);
  258. // if we reached the end of the chain, return false
  259. if (current == NO_SLOT)
  260. return false;
  261. count--;
  262. // remove slot from linked list
  263. // is slot at beginning of linked list?
  264. if (prev == NO_SLOT)
  265. table [index] = links [current].Next + 1;
  266. else
  267. links [prev].Next = links [current].Next;
  268. // mark slot as empty and prepend it to "empty slots chain"
  269. links [current].Next = empty_slot;
  270. empty_slot = current;
  271. // clear slot
  272. links [current].HashCode = 0;
  273. slots [current] = default (T);
  274. generation++;
  275. return true;
  276. }
  277. public int RemoveWhere (Predicate<T> predicate)
  278. {
  279. if (predicate == null)
  280. throw new ArgumentNullException ("predicate");
  281. int counter = 0;
  282. var copy = new T [count];
  283. CopyTo (copy, 0);
  284. foreach (var item in copy) {
  285. if (predicate (item)) {
  286. Remove (item);
  287. counter++;
  288. }
  289. }
  290. return counter;
  291. }
  292. public void TrimExcess ()
  293. {
  294. Resize ();
  295. }
  296. // set operations
  297. public void IntersectWith (IEnumerable<T> other)
  298. {
  299. if (other == null)
  300. throw new ArgumentNullException ("other");
  301. var copy = new T [count];
  302. CopyTo (copy, 0);
  303. foreach (var item in copy)
  304. if (!other.Contains (item))
  305. Remove (item);
  306. foreach (var item in other)
  307. if (!Contains (item))
  308. Remove (item);
  309. }
  310. public void ExceptWith (IEnumerable<T> other)
  311. {
  312. if (other == null)
  313. throw new ArgumentNullException ("other");
  314. foreach (var item in other)
  315. Remove (item);
  316. }
  317. public bool Overlaps (IEnumerable<T> other)
  318. {
  319. if (other == null)
  320. throw new ArgumentNullException ("other");
  321. foreach (var item in other)
  322. if (Contains (item))
  323. return true;
  324. return false;
  325. }
  326. public bool SetEquals (IEnumerable<T> other)
  327. {
  328. if (other == null)
  329. throw new ArgumentNullException ("other");
  330. if (count != other.Count ())
  331. return false;
  332. foreach (var item in this)
  333. if (!other.Contains (item))
  334. return false;
  335. return true;
  336. }
  337. public void SymmetricExceptWith (IEnumerable<T> other)
  338. {
  339. if (other == null)
  340. throw new ArgumentNullException ("other");
  341. foreach (var item in other) {
  342. if (Contains (item))
  343. Remove (item);
  344. else
  345. Add (item);
  346. }
  347. }
  348. public void UnionWith (IEnumerable<T> other)
  349. {
  350. if (other == null)
  351. throw new ArgumentNullException ("other");
  352. foreach (var item in other)
  353. Add (item);
  354. }
  355. bool CheckIsSubsetOf (IEnumerable<T> other)
  356. {
  357. if (other == null)
  358. throw new ArgumentNullException ("other");
  359. foreach (var item in this)
  360. if (!other.Contains (item))
  361. return false;
  362. return true;
  363. }
  364. public bool IsSubsetOf (IEnumerable<T> other)
  365. {
  366. if (other == null)
  367. throw new ArgumentNullException ("other");
  368. if (count == 0)
  369. return true;
  370. if (count > other.Count ())
  371. return false;
  372. return CheckIsSubsetOf (other);
  373. }
  374. public bool IsProperSubsetOf (IEnumerable<T> other)
  375. {
  376. if (other == null)
  377. throw new ArgumentNullException ("other");
  378. if (count == 0)
  379. return true;
  380. if (count >= other.Count ())
  381. return false;
  382. return CheckIsSubsetOf (other);
  383. }
  384. bool CheckIsSupersetOf (IEnumerable<T> other)
  385. {
  386. if (other == null)
  387. throw new ArgumentNullException ("other");
  388. foreach (var item in other)
  389. if (!Contains (item))
  390. return false;
  391. return true;
  392. }
  393. public bool IsSupersetOf (IEnumerable<T> other)
  394. {
  395. if (other == null)
  396. throw new ArgumentNullException ("other");
  397. if (count < other.Count ())
  398. return false;
  399. return CheckIsSupersetOf (other);
  400. }
  401. public bool IsProperSupersetOf (IEnumerable<T> other)
  402. {
  403. if (other == null)
  404. throw new ArgumentNullException ("other");
  405. if (count <= other.Count ())
  406. return false;
  407. return CheckIsSupersetOf (other);
  408. }
  409. [MonoTODO]
  410. public static IEqualityComparer<HashSet<T>> CreateSetComparer ()
  411. {
  412. throw new NotImplementedException ();
  413. }
  414. [MonoTODO]
  415. [SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
  416. public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
  417. {
  418. throw new NotImplementedException ();
  419. }
  420. [MonoTODO]
  421. public virtual void OnDeserialization (object sender)
  422. {
  423. if (si == null)
  424. return;
  425. throw new NotImplementedException ();
  426. }
  427. IEnumerator<T> IEnumerable<T>.GetEnumerator ()
  428. {
  429. return new Enumerator (this);
  430. }
  431. bool ICollection<T>.IsReadOnly {
  432. get { return false; }
  433. }
  434. void ICollection<T>.CopyTo (T [] array, int index)
  435. {
  436. CopyTo (array, index);
  437. }
  438. void ICollection<T>.Add (T item)
  439. {
  440. Add (item);
  441. }
  442. IEnumerator IEnumerable.GetEnumerator ()
  443. {
  444. return new Enumerator (this);
  445. }
  446. public Enumerator GetEnumerator ()
  447. {
  448. return new Enumerator (this);
  449. }
  450. [Serializable]
  451. public struct Enumerator : IEnumerator<T>, IDisposable {
  452. HashSet<T> hashset;
  453. int next;
  454. int stamp;
  455. T current;
  456. internal Enumerator (HashSet<T> hashset)
  457. : this ()
  458. {
  459. this.hashset = hashset;
  460. this.stamp = hashset.generation;
  461. }
  462. public bool MoveNext ()
  463. {
  464. CheckState ();
  465. if (next < 0)
  466. return false;
  467. while (next < hashset.touched) {
  468. int cur = next++;
  469. if (hashset.GetLinkHashCode (cur) != 0) {
  470. current = hashset.slots [cur];
  471. return true;
  472. }
  473. }
  474. next = NO_SLOT;
  475. return false;
  476. }
  477. public T Current {
  478. get { return current; }
  479. }
  480. object IEnumerator.Current {
  481. get {
  482. CheckState ();
  483. if (next <= 0)
  484. throw new InvalidOperationException ("Current is not valid");
  485. return current;
  486. }
  487. }
  488. void IEnumerator.Reset ()
  489. {
  490. CheckState ();
  491. next = 0;
  492. }
  493. public void Dispose ()
  494. {
  495. hashset = null;
  496. }
  497. void CheckState ()
  498. {
  499. if (hashset == null)
  500. throw new ObjectDisposedException (null);
  501. if (hashset.generation != stamp)
  502. throw new InvalidOperationException ("HashSet have been modified while it was iterated over");
  503. }
  504. }
  505. // borrowed from System.Collections.HashTable
  506. static class PrimeHelper {
  507. static readonly int [] primes_table = {
  508. 11,
  509. 19,
  510. 37,
  511. 73,
  512. 109,
  513. 163,
  514. 251,
  515. 367,
  516. 557,
  517. 823,
  518. 1237,
  519. 1861,
  520. 2777,
  521. 4177,
  522. 6247,
  523. 9371,
  524. 14057,
  525. 21089,
  526. 31627,
  527. 47431,
  528. 71143,
  529. 106721,
  530. 160073,
  531. 240101,
  532. 360163,
  533. 540217,
  534. 810343,
  535. 1215497,
  536. 1823231,
  537. 2734867,
  538. 4102283,
  539. 6153409,
  540. 9230113,
  541. 13845163
  542. };
  543. static bool TestPrime (int x)
  544. {
  545. if ((x & 1) != 0) {
  546. int top = (int) Math.Sqrt (x);
  547. for (int n = 3; n < top; n += 2) {
  548. if ((x % n) == 0)
  549. return false;
  550. }
  551. return true;
  552. }
  553. // There is only one even prime - 2.
  554. return x == 2;
  555. }
  556. static int CalcPrime (int x)
  557. {
  558. for (int i = (x & (~1)) - 1; i < Int32.MaxValue; i += 2)
  559. if (TestPrime (i))
  560. return i;
  561. return x;
  562. }
  563. public static int ToPrime (int x)
  564. {
  565. for (int i = 0; i < primes_table.Length; i++)
  566. if (x <= primes_table [i])
  567. return primes_table [i];
  568. return CalcPrime (x);
  569. }
  570. }
  571. }
  572. }