PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/corlib/System.Collections.Generic/Dictionary.cs

https://bitbucket.org/danipen/mono
C# | 1217 lines | 858 code | 209 blank | 150 comment | 153 complexity | 760b821aa5442adb626d46de64cb4258 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // System.Collections.Generic.Dictionary
  3. //
  4. // Authors:
  5. // Sureshkumar T (tsureshkumar@novell.com)
  6. // Marek Safar (marek.safar@gmail.com)
  7. // Ankit Jain (radical@corewars.org)
  8. // David Waite (mass@akuma.org)
  9. // Juraj Skripsky (js@hotfeet.ch)
  10. //
  11. //
  12. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  13. // Copyright (C) 2005 David Waite
  14. // Copyright (C) 2007 HotFeet GmbH (http://www.hotfeet.ch)
  15. // Copyright (C) 2011 Xamarin Inc (http://www.xamarin.com)
  16. //
  17. // Permission is hereby granted, free of charge, to any person obtaining
  18. // a copy of this software and associated documentation files (the
  19. // "Software"), to deal in the Software without restriction, including
  20. // without limitation the rights to use, copy, modify, merge, publish,
  21. // distribute, sublicense, and/or sell copies of the Software, and to
  22. // permit persons to whom the Software is furnished to do so, subject to
  23. // the following conditions:
  24. //
  25. // The above copyright notice and this permission notice shall be
  26. // included in all copies or substantial portions of the Software.
  27. //
  28. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  29. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  30. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  31. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  32. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  33. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  34. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  35. //
  36. using System;
  37. using System.Collections;
  38. using System.Collections.Generic;
  39. using System.Runtime.Serialization;
  40. using System.Security.Permissions;
  41. using System.Runtime.InteropServices;
  42. using System.Diagnostics;
  43. namespace System.Collections.Generic {
  44. /*
  45. * Declare this outside the main class so it doesn't have to be inflated for each
  46. * instantiation of Dictionary.
  47. */
  48. internal struct Link {
  49. public int HashCode;
  50. public int Next;
  51. }
  52. [ComVisible(false)]
  53. [Serializable]
  54. [DebuggerDisplay ("Count={Count}")]
  55. [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
  56. public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, ISerializable, IDeserializationCallback
  57. #if NET_4_5
  58. , IReadOnlyDictionary<TKey, TValue>
  59. #endif
  60. {
  61. // The implementation of this class uses a hash table and linked lists
  62. // (see: http://msdn2.microsoft.com/en-us/library/ms379571(VS.80).aspx).
  63. //
  64. // We use a kind of "mini-heap" instead of reference-based linked lists:
  65. // "keySlots" and "valueSlots" is the heap itself, it stores the data
  66. // "linkSlots" contains information about how the slots in the heap
  67. // are connected into linked lists
  68. // In addition, the HashCode field can be used to check if the
  69. // corresponding key and value are present (HashCode has the
  70. // HASH_FLAG bit set in this case), so, to iterate over all the
  71. // items in the dictionary, simply iterate the linkSlots array
  72. // and check for the HASH_FLAG bit in the HashCode field.
  73. // For this reason, each time a hashcode is calculated, it needs
  74. // to be ORed with HASH_FLAG before comparing it with the save hashcode.
  75. // "touchedSlots" and "emptySlot" manage the free space in the heap
  76. const int INITIAL_SIZE = 10;
  77. const float DEFAULT_LOAD_FACTOR = (90f / 100);
  78. const int NO_SLOT = -1;
  79. const int HASH_FLAG = -2147483648;
  80. // The hash table contains indices into the linkSlots array
  81. int [] table;
  82. // All (key,value) pairs are chained into linked lists. The connection
  83. // information is stored in "linkSlots" along with the key's hash code
  84. // (for performance reasons).
  85. // TODO: get rid of the hash code in Link (this depends on a few
  86. // JIT-compiler optimizations)
  87. // Every link in "linkSlots" corresponds to the (key,value) pair
  88. // in "keySlots"/"valueSlots" with the same index.
  89. Link [] linkSlots;
  90. TKey [] keySlots;
  91. TValue [] valueSlots;
  92. //Leave those 2 fields here to improve heap layout.
  93. IEqualityComparer<TKey> hcp;
  94. SerializationInfo serialization_info;
  95. // The number of slots in "linkSlots" and "keySlots"/"valueSlots" that
  96. // are in use (i.e. filled with data) or have been used and marked as
  97. // "empty" later on.
  98. int touchedSlots;
  99. // The index of the first slot in the "empty slots chain".
  100. // "Remove()" prepends the cleared slots to the empty chain.
  101. // "Add()" fills the first slot in the empty slots chain with the
  102. // added item (or increases "touchedSlots" if the chain itself is empty).
  103. int emptySlot;
  104. // The number of (key,value) pairs in this dictionary.
  105. int count;
  106. // The number of (key,value) pairs the dictionary can hold without
  107. // resizing the hash table and the slots arrays.
  108. int threshold;
  109. // The number of changes made to this dictionary. Used by enumerators
  110. // to detect changes and invalidate themselves.
  111. int generation;
  112. public int Count {
  113. get { return count; }
  114. }
  115. public TValue this [TKey key] {
  116. get {
  117. if (key == null)
  118. throw new ArgumentNullException ("key");
  119. // get first item of linked list corresponding to given key
  120. int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
  121. int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
  122. // walk linked list until right slot is found or end is reached
  123. while (cur != NO_SLOT) {
  124. // The ordering is important for compatibility with MS and strange
  125. // Object.Equals () implementations
  126. if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
  127. return valueSlots [cur];
  128. cur = linkSlots [cur].Next;
  129. }
  130. throw new KeyNotFoundException ();
  131. }
  132. set {
  133. if (key == null)
  134. throw new ArgumentNullException ("key");
  135. // get first item of linked list corresponding to given key
  136. int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
  137. int index = (hashCode & int.MaxValue) % table.Length;
  138. int cur = table [index] - 1;
  139. // walk linked list until right slot (and its predecessor) is
  140. // found or end is reached
  141. int prev = NO_SLOT;
  142. if (cur != NO_SLOT) {
  143. do {
  144. // The ordering is important for compatibility with MS and strange
  145. // Object.Equals () implementations
  146. if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
  147. break;
  148. prev = cur;
  149. cur = linkSlots [cur].Next;
  150. } while (cur != NO_SLOT);
  151. }
  152. // is there no slot for the given key yet?
  153. if (cur == NO_SLOT) {
  154. // there is no existing slot for the given key,
  155. // allocate one and prepend it to its corresponding linked
  156. // list
  157. if (++count > threshold) {
  158. Resize ();
  159. index = (hashCode & int.MaxValue) % table.Length;
  160. }
  161. // find an empty slot
  162. cur = emptySlot;
  163. if (cur == NO_SLOT)
  164. cur = touchedSlots++;
  165. else
  166. emptySlot = linkSlots [cur].Next;
  167. // prepend the added item to its linked list,
  168. // update the hash table
  169. linkSlots [cur].Next = table [index] - 1;
  170. table [index] = cur + 1;
  171. // store the new item and its hash code
  172. linkSlots [cur].HashCode = hashCode;
  173. keySlots [cur] = key;
  174. } else {
  175. // we already have a slot for the given key,
  176. // update the existing slot
  177. // if the slot is not at the front of its linked list,
  178. // we move it there
  179. if (prev != NO_SLOT) {
  180. linkSlots [prev].Next = linkSlots [cur].Next;
  181. linkSlots [cur].Next = table [index] - 1;
  182. table [index] = cur + 1;
  183. }
  184. }
  185. // store the item's data itself
  186. valueSlots [cur] = value;
  187. generation++;
  188. }
  189. }
  190. public Dictionary ()
  191. {
  192. Init (INITIAL_SIZE, null);
  193. }
  194. public Dictionary (IEqualityComparer<TKey> comparer)
  195. {
  196. Init (INITIAL_SIZE, comparer);
  197. }
  198. public Dictionary (IDictionary<TKey, TValue> dictionary)
  199. : this (dictionary, null)
  200. {
  201. }
  202. public Dictionary (int capacity)
  203. {
  204. Init (capacity, null);
  205. }
  206. public Dictionary (IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
  207. {
  208. if (dictionary == null)
  209. throw new ArgumentNullException ("dictionary");
  210. int capacity = dictionary.Count;
  211. Init (capacity, comparer);
  212. foreach (KeyValuePair<TKey, TValue> entry in dictionary)
  213. this.Add (entry.Key, entry.Value);
  214. }
  215. public Dictionary (int capacity, IEqualityComparer<TKey> comparer)
  216. {
  217. Init (capacity, comparer);
  218. }
  219. protected Dictionary (SerializationInfo info, StreamingContext context)
  220. {
  221. serialization_info = info;
  222. }
  223. private void Init (int capacity, IEqualityComparer<TKey> hcp)
  224. {
  225. if (capacity < 0)
  226. throw new ArgumentOutOfRangeException ("capacity");
  227. this.hcp = (hcp != null) ? hcp : EqualityComparer<TKey>.Default;
  228. if (capacity == 0)
  229. capacity = INITIAL_SIZE;
  230. /* Modify capacity so 'capacity' elements can be added without resizing */
  231. capacity = (int)(capacity / DEFAULT_LOAD_FACTOR) + 1;
  232. InitArrays (capacity);
  233. generation = 0;
  234. }
  235. private void InitArrays (int size) {
  236. table = new int [size];
  237. linkSlots = new Link [size];
  238. emptySlot = NO_SLOT;
  239. keySlots = new TKey [size];
  240. valueSlots = new TValue [size];
  241. touchedSlots = 0;
  242. threshold = (int)(table.Length * DEFAULT_LOAD_FACTOR);
  243. if (threshold == 0 && table.Length > 0)
  244. threshold = 1;
  245. }
  246. void CopyToCheck (Array array, int index)
  247. {
  248. if (array == null)
  249. throw new ArgumentNullException ("array");
  250. if (index < 0)
  251. throw new ArgumentOutOfRangeException ("index");
  252. // we want no exception for index==array.Length && Count == 0
  253. if (index > array.Length)
  254. throw new ArgumentException ("index larger than largest valid index of array");
  255. if (array.Length - index < Count)
  256. throw new ArgumentException ("Destination array cannot hold the requested elements!");
  257. }
  258. void CopyKeys (TKey[] array, int index)
  259. {
  260. for (int i = 0; i < touchedSlots; i++) {
  261. if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
  262. array [index++] = keySlots [i];
  263. }
  264. }
  265. void CopyValues (TValue[] array, int index)
  266. {
  267. for (int i = 0; i < touchedSlots; i++) {
  268. if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
  269. array [index++] = valueSlots [i];
  270. }
  271. }
  272. delegate TRet Transform<TRet> (TKey key, TValue value);
  273. static KeyValuePair<TKey, TValue> make_pair (TKey key, TValue value)
  274. {
  275. return new KeyValuePair<TKey, TValue> (key, value);
  276. }
  277. static TKey pick_key (TKey key, TValue value)
  278. {
  279. return key;
  280. }
  281. static TValue pick_value (TKey key, TValue value)
  282. {
  283. return value;
  284. }
  285. void CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
  286. {
  287. CopyToCheck (array, index);
  288. for (int i = 0; i < touchedSlots; i++) {
  289. if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
  290. array [index++] = new KeyValuePair<TKey, TValue> (keySlots [i], valueSlots [i]);
  291. }
  292. }
  293. void Do_ICollectionCopyTo<TRet> (Array array, int index, Transform<TRet> transform)
  294. {
  295. Type src = typeof (TRet);
  296. Type tgt = array.GetType ().GetElementType ();
  297. try {
  298. if ((src.IsPrimitive || tgt.IsPrimitive) && !tgt.IsAssignableFrom (src))
  299. throw new Exception (); // we don't care. it'll get transformed to an ArgumentException below
  300. #if BOOTSTRAP_BASIC
  301. // BOOTSTRAP: gmcs 2.4.x seems to have trouble compiling the alternative
  302. throw new Exception ();
  303. #else
  304. object[] dest = (object[])array;
  305. for (int i = 0; i < touchedSlots; i++) {
  306. if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
  307. dest [index++] = transform (keySlots [i], valueSlots [i]);
  308. }
  309. #endif
  310. } catch (Exception e) {
  311. throw new ArgumentException ("Cannot copy source collection elements to destination array", "array", e);
  312. }
  313. }
  314. private void Resize ()
  315. {
  316. // From the SDK docs:
  317. // Hashtable is automatically increased
  318. // to the smallest prime number that is larger
  319. // than twice the current number of Hashtable buckets
  320. int newSize = HashPrimeNumbers.ToPrime ((table.Length << 1) | 1);
  321. // allocate new hash table and link slots array
  322. int [] newTable = new int [newSize];
  323. Link [] newLinkSlots = new Link [newSize];
  324. for (int i = 0; i < table.Length; i++) {
  325. int cur = table [i] - 1;
  326. while (cur != NO_SLOT) {
  327. int hashCode = newLinkSlots [cur].HashCode = hcp.GetHashCode(keySlots [cur]) | HASH_FLAG;
  328. int index = (hashCode & int.MaxValue) % newSize;
  329. newLinkSlots [cur].Next = newTable [index] - 1;
  330. newTable [index] = cur + 1;
  331. cur = linkSlots [cur].Next;
  332. }
  333. }
  334. table = newTable;
  335. linkSlots = newLinkSlots;
  336. // allocate new data slots, copy data
  337. TKey [] newKeySlots = new TKey [newSize];
  338. TValue [] newValueSlots = new TValue [newSize];
  339. Array.Copy (keySlots, 0, newKeySlots, 0, touchedSlots);
  340. Array.Copy (valueSlots, 0, newValueSlots, 0, touchedSlots);
  341. keySlots = newKeySlots;
  342. valueSlots = newValueSlots;
  343. threshold = (int)(newSize * DEFAULT_LOAD_FACTOR);
  344. }
  345. public void Add (TKey key, TValue value)
  346. {
  347. if (key == null)
  348. throw new ArgumentNullException ("key");
  349. // get first item of linked list corresponding to given key
  350. int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
  351. int index = (hashCode & int.MaxValue) % table.Length;
  352. int cur = table [index] - 1;
  353. // walk linked list until end is reached (throw an exception if a
  354. // existing slot is found having an equivalent key)
  355. while (cur != NO_SLOT) {
  356. // The ordering is important for compatibility with MS and strange
  357. // Object.Equals () implementations
  358. if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
  359. throw new ArgumentException ("An element with the same key already exists in the dictionary.");
  360. cur = linkSlots [cur].Next;
  361. }
  362. if (++count > threshold) {
  363. Resize ();
  364. index = (hashCode & int.MaxValue) % table.Length;
  365. }
  366. // find an empty slot
  367. cur = emptySlot;
  368. if (cur == NO_SLOT)
  369. cur = touchedSlots++;
  370. else
  371. emptySlot = linkSlots [cur].Next;
  372. // store the hash code of the added item,
  373. // prepend the added item to its linked list,
  374. // update the hash table
  375. linkSlots [cur].HashCode = hashCode;
  376. linkSlots [cur].Next = table [index] - 1;
  377. table [index] = cur + 1;
  378. // store item's data
  379. keySlots [cur] = key;
  380. valueSlots [cur] = value;
  381. generation++;
  382. }
  383. public IEqualityComparer<TKey> Comparer {
  384. get { return hcp; }
  385. }
  386. public void Clear ()
  387. {
  388. count = 0;
  389. // clear the hash table
  390. Array.Clear (table, 0, table.Length);
  391. // clear arrays
  392. Array.Clear (keySlots, 0, keySlots.Length);
  393. Array.Clear (valueSlots, 0, valueSlots.Length);
  394. Array.Clear (linkSlots, 0, linkSlots.Length);
  395. // empty the "empty slots chain"
  396. emptySlot = NO_SLOT;
  397. touchedSlots = 0;
  398. generation++;
  399. }
  400. public bool ContainsKey (TKey key)
  401. {
  402. if (key == null)
  403. throw new ArgumentNullException ("key");
  404. // get first item of linked list corresponding to given key
  405. int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
  406. int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
  407. // walk linked list until right slot is found or end is reached
  408. while (cur != NO_SLOT) {
  409. // The ordering is important for compatibility with MS and strange
  410. // Object.Equals () implementations
  411. if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
  412. return true;
  413. cur = linkSlots [cur].Next;
  414. }
  415. return false;
  416. }
  417. public bool ContainsValue (TValue value)
  418. {
  419. IEqualityComparer<TValue> cmp = EqualityComparer<TValue>.Default;
  420. for (int i = 0; i < table.Length; i++) {
  421. int cur = table [i] - 1;
  422. while (cur != NO_SLOT) {
  423. if (cmp.Equals (valueSlots [cur], value))
  424. return true;
  425. cur = linkSlots [cur].Next;
  426. }
  427. }
  428. return false;
  429. }
  430. [SecurityPermission (SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
  431. public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
  432. {
  433. if (info == null)
  434. throw new ArgumentNullException ("info");
  435. info.AddValue ("Version", generation);
  436. info.AddValue ("Comparer", hcp);
  437. // MS.NET expects either *no* KeyValuePairs field (when count = 0)
  438. // or a non-null KeyValuePairs field. We don't omit the field to
  439. // remain compatible with older monos, but we also doesn't serialize
  440. // it as null to make MS.NET happy.
  441. KeyValuePair<TKey, TValue> [] data = new KeyValuePair<TKey,TValue> [count];
  442. if (count > 0)
  443. CopyTo (data, 0);
  444. info.AddValue ("HashSize", table.Length);
  445. info.AddValue ("KeyValuePairs", data);
  446. }
  447. public virtual void OnDeserialization (object sender)
  448. {
  449. if (serialization_info == null)
  450. return;
  451. int hashSize = 0;
  452. KeyValuePair<TKey, TValue> [] data = null;
  453. // We must use the enumerator because MS.NET doesn't
  454. // serialize "KeyValuePairs" for count = 0.
  455. SerializationInfoEnumerator e = serialization_info.GetEnumerator ();
  456. while (e.MoveNext ()) {
  457. switch (e.Name) {
  458. case "Version":
  459. generation = (int) e.Value;
  460. break;
  461. case "Comparer":
  462. hcp = (IEqualityComparer<TKey>) e.Value;
  463. break;
  464. case "HashSize":
  465. hashSize = (int) e.Value;
  466. break;
  467. case "KeyValuePairs":
  468. data = (KeyValuePair<TKey, TValue> []) e.Value;
  469. break;
  470. }
  471. }
  472. if (hcp == null)
  473. hcp = EqualityComparer<TKey>.Default;
  474. if (hashSize < INITIAL_SIZE)
  475. hashSize = INITIAL_SIZE;
  476. InitArrays (hashSize);
  477. count = 0;
  478. if (data != null) {
  479. for (int i = 0; i < data.Length; ++i)
  480. Add (data [i].Key, data [i].Value);
  481. }
  482. generation++;
  483. serialization_info = null;
  484. }
  485. public bool Remove (TKey key)
  486. {
  487. if (key == null)
  488. throw new ArgumentNullException ("key");
  489. // get first item of linked list corresponding to given key
  490. int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
  491. int index = (hashCode & int.MaxValue) % table.Length;
  492. int cur = table [index] - 1;
  493. // if there is no linked list, return false
  494. if (cur == NO_SLOT)
  495. return false;
  496. // walk linked list until right slot (and its predecessor) is
  497. // found or end is reached
  498. int prev = NO_SLOT;
  499. do {
  500. // The ordering is important for compatibility with MS and strange
  501. // Object.Equals () implementations
  502. if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
  503. break;
  504. prev = cur;
  505. cur = linkSlots [cur].Next;
  506. } while (cur != NO_SLOT);
  507. // if we reached the end of the chain, return false
  508. if (cur == NO_SLOT)
  509. return false;
  510. count--;
  511. // remove slot from linked list
  512. // is slot at beginning of linked list?
  513. if (prev == NO_SLOT)
  514. table [index] = linkSlots [cur].Next + 1;
  515. else
  516. linkSlots [prev].Next = linkSlots [cur].Next;
  517. // mark slot as empty and prepend it to "empty slots chain"
  518. linkSlots [cur].Next = emptySlot;
  519. emptySlot = cur;
  520. linkSlots [cur].HashCode = 0;
  521. // clear empty key and value slots
  522. keySlots [cur] = default (TKey);
  523. valueSlots [cur] = default (TValue);
  524. generation++;
  525. return true;
  526. }
  527. public bool TryGetValue (TKey key, out TValue value)
  528. {
  529. if (key == null)
  530. throw new ArgumentNullException ("key");
  531. // get first item of linked list corresponding to given key
  532. int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
  533. int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
  534. // walk linked list until right slot is found or end is reached
  535. while (cur != NO_SLOT) {
  536. // The ordering is important for compatibility with MS and strange
  537. // Object.Equals () implementations
  538. if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key)) {
  539. value = valueSlots [cur];
  540. return true;
  541. }
  542. cur = linkSlots [cur].Next;
  543. }
  544. // we did not find the slot
  545. value = default (TValue);
  546. return false;
  547. }
  548. ICollection<TKey> IDictionary<TKey, TValue>.Keys {
  549. get { return Keys; }
  550. }
  551. ICollection<TValue> IDictionary<TKey, TValue>.Values {
  552. get { return Values; }
  553. }
  554. #if NET_4_5
  555. IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys {
  556. get { return Keys; }
  557. }
  558. IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values {
  559. get { return Values; }
  560. }
  561. #endif
  562. public KeyCollection Keys {
  563. get { return new KeyCollection (this); }
  564. }
  565. public ValueCollection Values {
  566. get { return new ValueCollection (this); }
  567. }
  568. ICollection IDictionary.Keys {
  569. get { return Keys; }
  570. }
  571. ICollection IDictionary.Values {
  572. get { return Values; }
  573. }
  574. bool IDictionary.IsFixedSize {
  575. get { return false; }
  576. }
  577. bool IDictionary.IsReadOnly {
  578. get { return false; }
  579. }
  580. static TKey ToTKey (object key)
  581. {
  582. if (key == null)
  583. throw new ArgumentNullException ("key");
  584. if (!(key is TKey))
  585. throw new ArgumentException ("not of type: " + typeof (TKey).ToString (), "key");
  586. return (TKey) key;
  587. }
  588. static TValue ToTValue (object value)
  589. {
  590. if (value == null && !typeof (TValue).IsValueType)
  591. return default (TValue);
  592. if (!(value is TValue))
  593. throw new ArgumentException ("not of type: " + typeof (TValue).ToString (), "value");
  594. return (TValue) value;
  595. }
  596. object IDictionary.this [object key] {
  597. get {
  598. if (key is TKey && ContainsKey((TKey) key))
  599. return this [ToTKey (key)];
  600. return null;
  601. }
  602. set { this [ToTKey (key)] = ToTValue (value); }
  603. }
  604. void IDictionary.Add (object key, object value)
  605. {
  606. this.Add (ToTKey (key), ToTValue (value));
  607. }
  608. bool IDictionary.Contains (object key)
  609. {
  610. if (key == null)
  611. throw new ArgumentNullException ("key");
  612. if (key is TKey)
  613. return ContainsKey ((TKey) key);
  614. return false;
  615. }
  616. void IDictionary.Remove (object key)
  617. {
  618. if (key == null)
  619. throw new ArgumentNullException ("key");
  620. if (key is TKey)
  621. Remove ((TKey) key);
  622. }
  623. bool ICollection.IsSynchronized {
  624. get { return false; }
  625. }
  626. object ICollection.SyncRoot {
  627. get { return this; }
  628. }
  629. bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
  630. get { return false; }
  631. }
  632. void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair)
  633. {
  634. Add (keyValuePair.Key, keyValuePair.Value);
  635. }
  636. bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair)
  637. {
  638. return ContainsKeyValuePair (keyValuePair);
  639. }
  640. void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
  641. {
  642. this.CopyTo (array, index);
  643. }
  644. bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair)
  645. {
  646. if (!ContainsKeyValuePair (keyValuePair))
  647. return false;
  648. return Remove (keyValuePair.Key);
  649. }
  650. bool ContainsKeyValuePair (KeyValuePair<TKey, TValue> pair)
  651. {
  652. TValue value;
  653. if (!TryGetValue (pair.Key, out value))
  654. return false;
  655. return EqualityComparer<TValue>.Default.Equals (pair.Value, value);
  656. }
  657. void ICollection.CopyTo (Array array, int index)
  658. {
  659. KeyValuePair<TKey, TValue> [] pairs = array as KeyValuePair<TKey, TValue> [];
  660. if (pairs != null) {
  661. this.CopyTo (pairs, index);
  662. return;
  663. }
  664. CopyToCheck (array, index);
  665. DictionaryEntry [] entries = array as DictionaryEntry [];
  666. if (entries != null) {
  667. for (int i = 0; i < touchedSlots; i++) {
  668. if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
  669. entries [index++] = new DictionaryEntry (keySlots [i], valueSlots [i]);
  670. }
  671. return;
  672. }
  673. Do_ICollectionCopyTo<KeyValuePair<TKey, TValue>> (array, index, make_pair);
  674. }
  675. IEnumerator IEnumerable.GetEnumerator ()
  676. {
  677. return new Enumerator (this);
  678. }
  679. IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
  680. {
  681. return new Enumerator (this);
  682. }
  683. IDictionaryEnumerator IDictionary.GetEnumerator ()
  684. {
  685. return new ShimEnumerator (this);
  686. }
  687. public Enumerator GetEnumerator ()
  688. {
  689. return new Enumerator (this);
  690. }
  691. [Serializable]
  692. private class ShimEnumerator : IDictionaryEnumerator, IEnumerator
  693. {
  694. Enumerator host_enumerator;
  695. public ShimEnumerator (Dictionary<TKey, TValue> host)
  696. {
  697. host_enumerator = host.GetEnumerator ();
  698. }
  699. public void Dispose ()
  700. {
  701. host_enumerator.Dispose ();
  702. }
  703. public bool MoveNext ()
  704. {
  705. return host_enumerator.MoveNext ();
  706. }
  707. public DictionaryEntry Entry {
  708. get { return ((IDictionaryEnumerator) host_enumerator).Entry; }
  709. }
  710. public object Key {
  711. get { return host_enumerator.Current.Key; }
  712. }
  713. public object Value {
  714. get { return host_enumerator.Current.Value; }
  715. }
  716. // This is the raison d' etre of this $%!@$%@^@ class.
  717. // We want: IDictionary.GetEnumerator ().Current is DictionaryEntry
  718. public object Current {
  719. get { return Entry; }
  720. }
  721. public void Reset ()
  722. {
  723. host_enumerator.Reset ();
  724. }
  725. }
  726. [Serializable]
  727. public struct Enumerator : IEnumerator<KeyValuePair<TKey,TValue>>,
  728. IDisposable, IDictionaryEnumerator, IEnumerator
  729. {
  730. Dictionary<TKey, TValue> dictionary;
  731. int next;
  732. int stamp;
  733. internal KeyValuePair<TKey, TValue> current;
  734. internal Enumerator (Dictionary<TKey, TValue> dictionary)
  735. : this ()
  736. {
  737. this.dictionary = dictionary;
  738. stamp = dictionary.generation;
  739. }
  740. public bool MoveNext ()
  741. {
  742. VerifyState ();
  743. if (next < 0)
  744. return false;
  745. while (next < dictionary.touchedSlots) {
  746. int cur = next++;
  747. if ((dictionary.linkSlots [cur].HashCode & HASH_FLAG) != 0) {
  748. current = new KeyValuePair <TKey, TValue> (
  749. dictionary.keySlots [cur],
  750. dictionary.valueSlots [cur]
  751. );
  752. return true;
  753. }
  754. }
  755. next = -1;
  756. return false;
  757. }
  758. // No error checking happens. Usually, Current is immediately preceded by a MoveNext(), so it's wasteful to check again
  759. public KeyValuePair<TKey, TValue> Current {
  760. get { return current; }
  761. }
  762. internal TKey CurrentKey {
  763. get {
  764. VerifyCurrent ();
  765. return current.Key;
  766. }
  767. }
  768. internal TValue CurrentValue {
  769. get {
  770. VerifyCurrent ();
  771. return current.Value;
  772. }
  773. }
  774. object IEnumerator.Current {
  775. get {
  776. VerifyCurrent ();
  777. return current;
  778. }
  779. }
  780. void IEnumerator.Reset ()
  781. {
  782. Reset ();
  783. }
  784. internal void Reset ()
  785. {
  786. VerifyState ();
  787. next = 0;
  788. }
  789. DictionaryEntry IDictionaryEnumerator.Entry {
  790. get {
  791. VerifyCurrent ();
  792. return new DictionaryEntry (current.Key, current.Value);
  793. }
  794. }
  795. object IDictionaryEnumerator.Key {
  796. get { return CurrentKey; }
  797. }
  798. object IDictionaryEnumerator.Value {
  799. get { return CurrentValue; }
  800. }
  801. void VerifyState ()
  802. {
  803. if (dictionary == null)
  804. throw new ObjectDisposedException (null);
  805. if (dictionary.generation != stamp)
  806. throw new InvalidOperationException ("out of sync");
  807. }
  808. void VerifyCurrent ()
  809. {
  810. VerifyState ();
  811. if (next <= 0)
  812. throw new InvalidOperationException ("Current is not valid");
  813. }
  814. public void Dispose ()
  815. {
  816. dictionary = null;
  817. }
  818. }
  819. // This collection is a read only collection
  820. [Serializable]
  821. [DebuggerDisplay ("Count={Count}")]
  822. [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
  823. public sealed class KeyCollection : ICollection<TKey>, IEnumerable<TKey>, ICollection, IEnumerable {
  824. Dictionary<TKey, TValue> dictionary;
  825. public KeyCollection (Dictionary<TKey, TValue> dictionary)
  826. {
  827. if (dictionary == null)
  828. throw new ArgumentNullException ("dictionary");
  829. this.dictionary = dictionary;
  830. }
  831. public void CopyTo (TKey [] array, int index)
  832. {
  833. dictionary.CopyToCheck (array, index);
  834. dictionary.CopyKeys (array, index);
  835. }
  836. public Enumerator GetEnumerator ()
  837. {
  838. return new Enumerator (dictionary);
  839. }
  840. void ICollection<TKey>.Add (TKey item)
  841. {
  842. throw new NotSupportedException ("this is a read-only collection");
  843. }
  844. void ICollection<TKey>.Clear ()
  845. {
  846. throw new NotSupportedException ("this is a read-only collection");
  847. }
  848. bool ICollection<TKey>.Contains (TKey item)
  849. {
  850. return dictionary.ContainsKey (item);
  851. }
  852. bool ICollection<TKey>.Remove (TKey item)
  853. {
  854. throw new NotSupportedException ("this is a read-only collection");
  855. }
  856. IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator ()
  857. {
  858. return this.GetEnumerator ();
  859. }
  860. void ICollection.CopyTo (Array array, int index)
  861. {
  862. var target = array as TKey [];
  863. if (target != null) {
  864. CopyTo (target, index);
  865. return;
  866. }
  867. dictionary.CopyToCheck (array, index);
  868. dictionary.Do_ICollectionCopyTo<TKey> (array, index, pick_key);
  869. }
  870. IEnumerator IEnumerable.GetEnumerator ()
  871. {
  872. return this.GetEnumerator ();
  873. }
  874. public int Count {
  875. get { return dictionary.Count; }
  876. }
  877. bool ICollection<TKey>.IsReadOnly {
  878. get { return true; }
  879. }
  880. bool ICollection.IsSynchronized {
  881. get { return false; }
  882. }
  883. object ICollection.SyncRoot {
  884. get { return ((ICollection) dictionary).SyncRoot; }
  885. }
  886. [Serializable]
  887. public struct Enumerator : IEnumerator<TKey>, IDisposable, IEnumerator {
  888. Dictionary<TKey, TValue>.Enumerator host_enumerator;
  889. internal Enumerator (Dictionary<TKey, TValue> host)
  890. {
  891. host_enumerator = host.GetEnumerator ();
  892. }
  893. public void Dispose ()
  894. {
  895. host_enumerator.Dispose ();
  896. }
  897. public bool MoveNext ()
  898. {
  899. return host_enumerator.MoveNext ();
  900. }
  901. public TKey Current {
  902. get { return host_enumerator.current.Key; }
  903. }
  904. object IEnumerator.Current {
  905. get { return host_enumerator.CurrentKey; }
  906. }
  907. void IEnumerator.Reset ()
  908. {
  909. host_enumerator.Reset ();
  910. }
  911. }
  912. }
  913. // This collection is a read only collection
  914. [Serializable]
  915. [DebuggerDisplay ("Count={Count}")]
  916. [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
  917. public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable {
  918. Dictionary<TKey, TValue> dictionary;
  919. public ValueCollection (Dictionary<TKey, TValue> dictionary)
  920. {
  921. if (dictionary == null)
  922. throw new ArgumentNullException ("dictionary");
  923. this.dictionary = dictionary;
  924. }
  925. public void CopyTo (TValue [] array, int index)
  926. {
  927. dictionary.CopyToCheck (array, index);
  928. dictionary.CopyValues (array, index);
  929. }
  930. public Enumerator GetEnumerator ()
  931. {
  932. return new Enumerator (dictionary);
  933. }
  934. void ICollection<TValue>.Add (TValue item)
  935. {
  936. throw new NotSupportedException ("this is a read-only collection");
  937. }
  938. void ICollection<TValue>.Clear ()
  939. {
  940. throw new NotSupportedException ("this is a read-only collection");
  941. }
  942. bool ICollection<TValue>.Contains (TValue item)
  943. {
  944. return dictionary.ContainsValue (item);
  945. }
  946. bool ICollection<TValue>.Remove (TValue item)
  947. {
  948. throw new NotSupportedException ("this is a read-only collection");
  949. }
  950. IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator ()
  951. {
  952. return this.GetEnumerator ();
  953. }
  954. void ICollection.CopyTo (Array array, int index)
  955. {
  956. var target = array as TValue [];
  957. if (target != null) {
  958. CopyTo (target, index);
  959. return;
  960. }
  961. dictionary.CopyToCheck (array, index);
  962. dictionary.Do_ICollectionCopyTo<TValue> (array, index, pick_value);
  963. }
  964. IEnumerator IEnumerable.GetEnumerator ()
  965. {
  966. return this.GetEnumerator ();
  967. }
  968. public int Count {
  969. get { return dictionary.Count; }
  970. }
  971. bool ICollection<TValue>.IsReadOnly {
  972. get { return true; }
  973. }
  974. bool ICollection.IsSynchronized {
  975. get { return false; }
  976. }
  977. object ICollection.SyncRoot {
  978. get { return ((ICollection) dictionary).SyncRoot; }
  979. }
  980. [Serializable]
  981. public struct Enumerator : IEnumerator<TValue>, IDisposable, IEnumerator {
  982. Dictionary<TKey, TValue>.Enumerator host_enumerator;
  983. internal Enumerator (Dictionary<TKey,TValue> host)
  984. {
  985. host_enumerator = host.GetEnumerator ();
  986. }
  987. public void Dispose ()
  988. {
  989. host_enumerator.Dispose ();
  990. }
  991. public bool MoveNext ()
  992. {
  993. return host_enumerator.MoveNext ();
  994. }
  995. public TValue Current {
  996. get { return host_enumerator.current.Value; }
  997. }
  998. object IEnumerator.Current {
  999. get { return host_enumerator.CurrentValue; }
  1000. }
  1001. void IEnumerator.Reset ()
  1002. {
  1003. host_enumerator.Reset ();
  1004. }
  1005. }
  1006. }
  1007. }
  1008. }