disable broken tests on net_4_0
[mcs.git] / class / corlib / System.Collections.Generic / Dictionary.cs
blob65d88dbde3731eb7fb3943b8ab443a97d3562399
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)
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)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System;
38 using System.Collections;
39 using System.Collections.Generic;
40 using System.Runtime.Serialization;
41 using System.Security.Permissions;
42 using System.Runtime.InteropServices;
43 using System.Diagnostics;
45 namespace System.Collections.Generic {
47 /*
48 * Declare this outside the main class so it doesn't have to be inflated for each
49 * instantiation of Dictionary.
51 internal struct Link {
52 public int HashCode;
53 public int Next;
56 [ComVisible(false)]
57 [Serializable]
58 [DebuggerDisplay ("Count={Count}")]
59 [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
60 public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,
61 IDictionary,
62 ICollection,
63 ICollection<KeyValuePair<TKey, TValue>>,
64 IEnumerable<KeyValuePair<TKey, TValue>>,
65 ISerializable,
66 IDeserializationCallback
68 // The implementation of this class uses a hash table and linked lists
69 // (see: http://msdn2.microsoft.com/en-us/library/ms379571(VS.80).aspx).
70 //
71 // We use a kind of "mini-heap" instead of reference-based linked lists:
72 // "keySlots" and "valueSlots" is the heap itself, it stores the data
73 // "linkSlots" contains information about how the slots in the heap
74 // are connected into linked lists
75 // In addition, the HashCode field can be used to check if the
76 // corresponding key and value are present (HashCode has the
77 // HASH_FLAG bit set in this case), so, to iterate over all the
78 // items in the dictionary, simply iterate the linkSlots array
79 // and check for the HASH_FLAG bit in the HashCode field.
80 // For this reason, each time a hashcode is calculated, it needs
81 // to be ORed with HASH_FLAG before comparing it with the save hashcode.
82 // "touchedSlots" and "emptySlot" manage the free space in the heap
84 const int INITIAL_SIZE = 10;
85 const float DEFAULT_LOAD_FACTOR = (90f / 100);
86 const int NO_SLOT = -1;
87 const int HASH_FLAG = -2147483648;
89 // The hash table contains indices into the linkSlots array
90 int [] table;
92 // All (key,value) pairs are chained into linked lists. The connection
93 // information is stored in "linkSlots" along with the key's hash code
94 // (for performance reasons).
95 // TODO: get rid of the hash code in Link (this depends on a few
96 // JIT-compiler optimizations)
97 // Every link in "linkSlots" corresponds to the (key,value) pair
98 // in "keySlots"/"valueSlots" with the same index.
99 Link [] linkSlots;
100 TKey [] keySlots;
101 TValue [] valueSlots;
103 // The number of slots in "linkSlots" and "keySlots"/"valueSlots" that
104 // are in use (i.e. filled with data) or have been used and marked as
105 // "empty" later on.
106 int touchedSlots;
108 // The index of the first slot in the "empty slots chain".
109 // "Remove()" prepends the cleared slots to the empty chain.
110 // "Add()" fills the first slot in the empty slots chain with the
111 // added item (or increases "touchedSlots" if the chain itself is empty).
112 int emptySlot;
114 // The number of (key,value) pairs in this dictionary.
115 int count;
117 // The number of (key,value) pairs the dictionary can hold without
118 // resizing the hash table and the slots arrays.
119 int threshold;
121 IEqualityComparer<TKey> hcp;
122 SerializationInfo serialization_info;
124 // The number of changes made to this dictionary. Used by enumerators
125 // to detect changes and invalidate themselves.
126 int generation;
128 public int Count {
129 get { return count; }
132 public TValue this [TKey key] {
133 get {
134 if (key == null)
135 throw new ArgumentNullException ("key");
137 // get first item of linked list corresponding to given key
138 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
139 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
141 // walk linked list until right slot is found or end is reached
142 while (cur != NO_SLOT) {
143 // The ordering is important for compatibility with MS and strange
144 // Object.Equals () implementations
145 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
146 return valueSlots [cur];
147 cur = linkSlots [cur].Next;
149 throw new KeyNotFoundException ();
152 set {
153 if (key == null)
154 throw new ArgumentNullException ("key");
156 // get first item of linked list corresponding to given key
157 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
158 int index = (hashCode & int.MaxValue) % table.Length;
159 int cur = table [index] - 1;
161 // walk linked list until right slot (and its predecessor) is
162 // found or end is reached
163 int prev = NO_SLOT;
164 if (cur != NO_SLOT) {
165 do {
166 // The ordering is important for compatibility with MS and strange
167 // Object.Equals () implementations
168 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
169 break;
170 prev = cur;
171 cur = linkSlots [cur].Next;
172 } while (cur != NO_SLOT);
175 // is there no slot for the given key yet?
176 if (cur == NO_SLOT) {
177 // there is no existing slot for the given key,
178 // allocate one and prepend it to its corresponding linked
179 // list
181 if (++count > threshold) {
182 Resize ();
183 index = (hashCode & int.MaxValue) % table.Length;
186 // find an empty slot
187 cur = emptySlot;
188 if (cur == NO_SLOT)
189 cur = touchedSlots++;
190 else
191 emptySlot = linkSlots [cur].Next;
193 // prepend the added item to its linked list,
194 // update the hash table
195 linkSlots [cur].Next = table [index] - 1;
196 table [index] = cur + 1;
198 // store the new item and its hash code
199 linkSlots [cur].HashCode = hashCode;
200 keySlots [cur] = key;
201 } else {
202 // we already have a slot for the given key,
203 // update the existing slot
205 // if the slot is not at the front of its linked list,
206 // we move it there
207 if (prev != NO_SLOT) {
208 linkSlots [prev].Next = linkSlots [cur].Next;
209 linkSlots [cur].Next = table [index] - 1;
210 table [index] = cur + 1;
214 // store the item's data itself
215 valueSlots [cur] = value;
217 generation++;
221 public Dictionary ()
223 Init (INITIAL_SIZE, null);
226 public Dictionary (IEqualityComparer<TKey> comparer)
228 Init (INITIAL_SIZE, comparer);
231 public Dictionary (IDictionary<TKey, TValue> dictionary)
232 : this (dictionary, null)
236 public Dictionary (int capacity)
238 Init (capacity, null);
241 public Dictionary (IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
243 if (dictionary == null)
244 throw new ArgumentNullException ("dictionary");
245 int capacity = dictionary.Count;
246 Init (capacity, comparer);
247 foreach (KeyValuePair<TKey, TValue> entry in dictionary)
248 this.Add (entry.Key, entry.Value);
251 public Dictionary (int capacity, IEqualityComparer<TKey> comparer)
253 Init (capacity, comparer);
256 protected Dictionary (SerializationInfo info, StreamingContext context)
258 serialization_info = info;
261 private void Init (int capacity, IEqualityComparer<TKey> hcp)
263 if (capacity < 0)
264 throw new ArgumentOutOfRangeException ("capacity");
265 this.hcp = (hcp != null) ? hcp : EqualityComparer<TKey>.Default;
266 if (capacity == 0)
267 capacity = INITIAL_SIZE;
269 /* Modify capacity so 'capacity' elements can be added without resizing */
270 capacity = (int)(capacity / DEFAULT_LOAD_FACTOR) + 1;
272 InitArrays (capacity);
273 generation = 0;
276 private void InitArrays (int size) {
277 table = new int [size];
279 linkSlots = new Link [size];
280 emptySlot = NO_SLOT;
282 keySlots = new TKey [size];
283 valueSlots = new TValue [size];
284 touchedSlots = 0;
286 threshold = (int)(table.Length * DEFAULT_LOAD_FACTOR);
287 if (threshold == 0 && table.Length > 0)
288 threshold = 1;
291 void CopyToCheck (Array array, int index)
293 if (array == null)
294 throw new ArgumentNullException ("array");
295 if (index < 0)
296 throw new ArgumentOutOfRangeException ("index");
297 // we want no exception for index==array.Length && Count == 0
298 if (index > array.Length)
299 throw new ArgumentException ("index larger than largest valid index of array");
300 if (array.Length - index < Count)
301 throw new ArgumentException ("Destination array cannot hold the requested elements!");
304 delegate TRet Transform<TRet> (TKey key, TValue value);
306 void Do_CopyTo<TRet, TElem> (TElem [] array, int index, Transform<TRet> transform)
307 where TRet : TElem
309 for (int i = 0; i < touchedSlots; i++) {
310 if ((linkSlots [i].HashCode & HASH_FLAG) != 0)
311 array [index++] = transform (keySlots [i], valueSlots [i]);
315 static KeyValuePair<TKey, TValue> make_pair (TKey key, TValue value)
317 return new KeyValuePair<TKey, TValue> (key, value);
320 static TKey pick_key (TKey key, TValue value)
322 return key;
325 static TValue pick_value (TKey key, TValue value)
327 return value;
330 void CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
332 CopyToCheck (array, index);
333 Do_CopyTo<KeyValuePair<TKey, TValue>, KeyValuePair<TKey, TValue>> (array, index, make_pair);
336 void Do_ICollectionCopyTo<TRet> (Array array, int index, Transform<TRet> transform)
338 Type src = typeof (TRet);
339 Type tgt = array.GetType ().GetElementType ();
341 try {
342 if ((src.IsPrimitive || tgt.IsPrimitive) && !tgt.IsAssignableFrom (src))
343 throw new Exception (); // we don't care. it'll get transformed to an ArgumentException below
345 #if BOOTSTRAP_BASIC
346 // BOOTSTRAP: gmcs 2.4.x seems to have trouble compiling the alternative
347 throw new Exception ();
348 #else
349 Do_CopyTo ((object []) array, index, transform);
350 #endif
352 } catch (Exception e) {
353 throw new ArgumentException ("Cannot copy source collection elements to destination array", "array", e);
357 private void Resize ()
359 // From the SDK docs:
360 // Hashtable is automatically increased
361 // to the smallest prime number that is larger
362 // than twice the current number of Hashtable buckets
363 int newSize = Hashtable.ToPrime ((table.Length << 1) | 1);
365 // allocate new hash table and link slots array
366 int [] newTable = new int [newSize];
367 Link [] newLinkSlots = new Link [newSize];
369 for (int i = 0; i < table.Length; i++) {
370 int cur = table [i] - 1;
371 while (cur != NO_SLOT) {
372 int hashCode = newLinkSlots [cur].HashCode = hcp.GetHashCode(keySlots [cur]) | HASH_FLAG;
373 int index = (hashCode & int.MaxValue) % newSize;
374 newLinkSlots [cur].Next = newTable [index] - 1;
375 newTable [index] = cur + 1;
376 cur = linkSlots [cur].Next;
379 table = newTable;
380 linkSlots = newLinkSlots;
382 // allocate new data slots, copy data
383 TKey [] newKeySlots = new TKey [newSize];
384 TValue [] newValueSlots = new TValue [newSize];
385 Array.Copy (keySlots, 0, newKeySlots, 0, touchedSlots);
386 Array.Copy (valueSlots, 0, newValueSlots, 0, touchedSlots);
387 keySlots = newKeySlots;
388 valueSlots = newValueSlots;
390 threshold = (int)(newSize * DEFAULT_LOAD_FACTOR);
393 public void Add (TKey key, TValue value)
395 if (key == null)
396 throw new ArgumentNullException ("key");
398 // get first item of linked list corresponding to given key
399 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
400 int index = (hashCode & int.MaxValue) % table.Length;
401 int cur = table [index] - 1;
403 // walk linked list until end is reached (throw an exception if a
404 // existing slot is found having an equivalent key)
405 while (cur != NO_SLOT) {
406 // The ordering is important for compatibility with MS and strange
407 // Object.Equals () implementations
408 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
409 throw new ArgumentException ("An element with the same key already exists in the dictionary.");
410 cur = linkSlots [cur].Next;
413 if (++count > threshold) {
414 Resize ();
415 index = (hashCode & int.MaxValue) % table.Length;
418 // find an empty slot
419 cur = emptySlot;
420 if (cur == NO_SLOT)
421 cur = touchedSlots++;
422 else
423 emptySlot = linkSlots [cur].Next;
425 // store the hash code of the added item,
426 // prepend the added item to its linked list,
427 // update the hash table
428 linkSlots [cur].HashCode = hashCode;
429 linkSlots [cur].Next = table [index] - 1;
430 table [index] = cur + 1;
432 // store item's data
433 keySlots [cur] = key;
434 valueSlots [cur] = value;
436 generation++;
439 public IEqualityComparer<TKey> Comparer {
440 get { return hcp; }
443 public void Clear ()
445 count = 0;
446 // clear the hash table
447 Array.Clear (table, 0, table.Length);
448 // clear arrays
449 Array.Clear (keySlots, 0, keySlots.Length);
450 Array.Clear (valueSlots, 0, valueSlots.Length);
451 Array.Clear (linkSlots, 0, linkSlots.Length);
453 // empty the "empty slots chain"
454 emptySlot = NO_SLOT;
456 touchedSlots = 0;
457 generation++;
460 public bool ContainsKey (TKey key)
462 if (key == null)
463 throw new ArgumentNullException ("key");
465 // get first item of linked list corresponding to given key
466 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
467 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
469 // walk linked list until right slot is found or end is reached
470 while (cur != NO_SLOT) {
471 // The ordering is important for compatibility with MS and strange
472 // Object.Equals () implementations
473 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
474 return true;
475 cur = linkSlots [cur].Next;
478 return false;
481 public bool ContainsValue (TValue value)
483 IEqualityComparer<TValue> cmp = EqualityComparer<TValue>.Default;
485 for (int i = 0; i < table.Length; i++) {
486 int cur = table [i] - 1;
487 while (cur != NO_SLOT) {
488 if (cmp.Equals (valueSlots [cur], value))
489 return true;
490 cur = linkSlots [cur].Next;
493 return false;
496 [SecurityPermission (SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
497 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
499 if (info == null)
500 throw new ArgumentNullException ("info");
502 info.AddValue ("Version", generation);
503 info.AddValue ("Comparer", hcp);
504 // MS.NET expects either *no* KeyValuePairs field (when count = 0)
505 // or a non-null KeyValuePairs field. We don't omit the field to
506 // remain compatible with older monos, but we also doesn't serialize
507 // it as null to make MS.NET happy.
508 KeyValuePair<TKey, TValue> [] data = new KeyValuePair<TKey,TValue> [count];
509 if (count > 0)
510 CopyTo (data, 0);
511 info.AddValue ("HashSize", table.Length);
512 info.AddValue ("KeyValuePairs", data);
515 public virtual void OnDeserialization (object sender)
517 if (serialization_info == null)
518 return;
520 int hashSize = 0;
521 KeyValuePair<TKey, TValue> [] data = null;
523 // We must use the enumerator because MS.NET doesn't
524 // serialize "KeyValuePairs" for count = 0.
525 SerializationInfoEnumerator e = serialization_info.GetEnumerator ();
526 while (e.MoveNext ()) {
527 switch (e.Name) {
528 case "Version":
529 generation = (int) e.Value;
530 break;
532 case "Comparer":
533 hcp = (IEqualityComparer<TKey>) e.Value;
534 break;
536 case "HashSize":
537 hashSize = (int) e.Value;
538 break;
540 case "KeyValuePairs":
541 data = (KeyValuePair<TKey, TValue> []) e.Value;
542 break;
546 if (hcp == null)
547 hcp = EqualityComparer<TKey>.Default;
548 if (hashSize < INITIAL_SIZE)
549 hashSize = INITIAL_SIZE;
550 InitArrays (hashSize);
551 count = 0;
553 if (data != null) {
554 for (int i = 0; i < data.Length; ++i)
555 Add (data [i].Key, data [i].Value);
557 generation++;
558 serialization_info = null;
561 public bool Remove (TKey key)
563 if (key == null)
564 throw new ArgumentNullException ("key");
566 // get first item of linked list corresponding to given key
567 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
568 int index = (hashCode & int.MaxValue) % table.Length;
569 int cur = table [index] - 1;
571 // if there is no linked list, return false
572 if (cur == NO_SLOT)
573 return false;
575 // walk linked list until right slot (and its predecessor) is
576 // found or end is reached
577 int prev = NO_SLOT;
578 do {
579 // The ordering is important for compatibility with MS and strange
580 // Object.Equals () implementations
581 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key))
582 break;
583 prev = cur;
584 cur = linkSlots [cur].Next;
585 } while (cur != NO_SLOT);
587 // if we reached the end of the chain, return false
588 if (cur == NO_SLOT)
589 return false;
591 count--;
592 // remove slot from linked list
593 // is slot at beginning of linked list?
594 if (prev == NO_SLOT)
595 table [index] = linkSlots [cur].Next + 1;
596 else
597 linkSlots [prev].Next = linkSlots [cur].Next;
599 // mark slot as empty and prepend it to "empty slots chain"
600 linkSlots [cur].Next = emptySlot;
601 emptySlot = cur;
603 linkSlots [cur].HashCode = 0;
604 // clear empty key and value slots
605 keySlots [cur] = default (TKey);
606 valueSlots [cur] = default (TValue);
608 generation++;
609 return true;
612 public bool TryGetValue (TKey key, out TValue value)
614 if (key == null)
615 throw new ArgumentNullException ("key");
617 // get first item of linked list corresponding to given key
618 int hashCode = hcp.GetHashCode (key) | HASH_FLAG;
619 int cur = table [(hashCode & int.MaxValue) % table.Length] - 1;
621 // walk linked list until right slot is found or end is reached
622 while (cur != NO_SLOT) {
623 // The ordering is important for compatibility with MS and strange
624 // Object.Equals () implementations
625 if (linkSlots [cur].HashCode == hashCode && hcp.Equals (keySlots [cur], key)) {
626 value = valueSlots [cur];
627 return true;
629 cur = linkSlots [cur].Next;
632 // we did not find the slot
633 value = default (TValue);
634 return false;
637 ICollection<TKey> IDictionary<TKey, TValue>.Keys {
638 get { return Keys; }
641 ICollection<TValue> IDictionary<TKey, TValue>.Values {
642 get { return Values; }
645 public KeyCollection Keys {
646 get { return new KeyCollection (this); }
649 public ValueCollection Values {
650 get { return new ValueCollection (this); }
653 ICollection IDictionary.Keys {
654 get { return Keys; }
657 ICollection IDictionary.Values {
658 get { return Values; }
661 bool IDictionary.IsFixedSize {
662 get { return false; }
665 bool IDictionary.IsReadOnly {
666 get { return false; }
669 TKey ToTKey (object key)
671 if (key == null)
672 throw new ArgumentNullException ("key");
673 if (!(key is TKey))
674 throw new ArgumentException ("not of type: " + typeof (TKey).ToString (), "key");
675 return (TKey) key;
678 TValue ToTValue (object value)
680 if (value == null && !typeof (TValue).IsValueType)
681 return default (TValue);
682 if (!(value is TValue))
683 throw new ArgumentException ("not of type: " + typeof (TValue).ToString (), "value");
684 return (TValue) value;
687 object IDictionary.this [object key] {
688 get {
689 if (key is TKey && ContainsKey((TKey) key))
690 return this [ToTKey (key)];
691 return null;
693 set { this [ToTKey (key)] = ToTValue (value); }
696 void IDictionary.Add (object key, object value)
698 this.Add (ToTKey (key), ToTValue (value));
701 bool IDictionary.Contains (object key)
703 if (key == null)
704 throw new ArgumentNullException ("key");
705 if (key is TKey)
706 return ContainsKey ((TKey) key);
707 return false;
710 void IDictionary.Remove (object key)
712 if (key == null)
713 throw new ArgumentNullException ("key");
714 if (key is TKey)
715 Remove ((TKey) key);
718 bool ICollection.IsSynchronized {
719 get { return false; }
722 object ICollection.SyncRoot {
723 get { return this; }
726 bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
727 get { return false; }
730 void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> keyValuePair)
732 Add (keyValuePair.Key, keyValuePair.Value);
735 bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair)
737 return ContainsKeyValuePair (keyValuePair);
740 void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue> [] array, int index)
742 this.CopyTo (array, index);
745 bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> keyValuePair)
747 if (!ContainsKeyValuePair (keyValuePair))
748 return false;
750 return Remove (keyValuePair.Key);
753 bool ContainsKeyValuePair (KeyValuePair<TKey, TValue> pair)
755 TValue value;
756 if (!TryGetValue (pair.Key, out value))
757 return false;
759 return EqualityComparer<TValue>.Default.Equals (pair.Value, value);
762 void ICollection.CopyTo (Array array, int index)
764 KeyValuePair<TKey, TValue> [] pairs = array as KeyValuePair<TKey, TValue> [];
765 if (pairs != null) {
766 this.CopyTo (pairs, index);
767 return;
770 CopyToCheck (array, index);
771 DictionaryEntry [] entries = array as DictionaryEntry [];
772 if (entries != null) {
773 Do_CopyTo (entries, index, delegate (TKey key, TValue value) { return new DictionaryEntry (key, value); });
774 return;
777 Do_ICollectionCopyTo<KeyValuePair<TKey, TValue>> (array, index, make_pair);
780 IEnumerator IEnumerable.GetEnumerator ()
782 return new Enumerator (this);
785 IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
787 return new Enumerator (this);
790 IDictionaryEnumerator IDictionary.GetEnumerator ()
792 return new ShimEnumerator (this);
795 public Enumerator GetEnumerator ()
797 return new Enumerator (this);
800 [Serializable]
801 private class ShimEnumerator : IDictionaryEnumerator, IEnumerator
803 Enumerator host_enumerator;
804 public ShimEnumerator (Dictionary<TKey, TValue> host)
806 host_enumerator = host.GetEnumerator ();
809 public void Dispose ()
811 host_enumerator.Dispose ();
814 public bool MoveNext ()
816 return host_enumerator.MoveNext ();
819 public DictionaryEntry Entry {
820 get { return ((IDictionaryEnumerator) host_enumerator).Entry; }
823 public object Key {
824 get { return host_enumerator.Current.Key; }
827 public object Value {
828 get { return host_enumerator.Current.Value; }
831 // This is the raison d' etre of this $%!@$%@^@ class.
832 // We want: IDictionary.GetEnumerator ().Current is DictionaryEntry
833 public object Current {
834 get { return Entry; }
837 public void Reset ()
839 host_enumerator.Reset ();
843 [Serializable]
844 public struct Enumerator : IEnumerator<KeyValuePair<TKey,TValue>>,
845 IDisposable, IDictionaryEnumerator, IEnumerator
847 Dictionary<TKey, TValue> dictionary;
848 int next;
849 int stamp;
851 internal KeyValuePair<TKey, TValue> current;
853 internal Enumerator (Dictionary<TKey, TValue> dictionary)
854 : this ()
856 this.dictionary = dictionary;
857 stamp = dictionary.generation;
860 public bool MoveNext ()
862 VerifyState ();
864 if (next < 0)
865 return false;
867 while (next < dictionary.touchedSlots) {
868 int cur = next++;
869 if ((dictionary.linkSlots [cur].HashCode & HASH_FLAG) != 0) {
870 current = new KeyValuePair <TKey, TValue> (
871 dictionary.keySlots [cur],
872 dictionary.valueSlots [cur]
874 return true;
878 next = -1;
879 return false;
882 // No error checking happens. Usually, Current is immediately preceded by a MoveNext(), so it's wasteful to check again
883 public KeyValuePair<TKey, TValue> Current {
884 get { return current; }
887 internal TKey CurrentKey {
888 get {
889 VerifyCurrent ();
890 return current.Key;
894 internal TValue CurrentValue {
895 get {
896 VerifyCurrent ();
897 return current.Value;
901 object IEnumerator.Current {
902 get {
903 VerifyCurrent ();
904 return current;
908 void IEnumerator.Reset ()
910 Reset ();
913 internal void Reset ()
915 VerifyState ();
916 next = 0;
919 DictionaryEntry IDictionaryEnumerator.Entry {
920 get {
921 VerifyCurrent ();
922 return new DictionaryEntry (current.Key, current.Value);
926 object IDictionaryEnumerator.Key {
927 get { return CurrentKey; }
930 object IDictionaryEnumerator.Value {
931 get { return CurrentValue; }
934 void VerifyState ()
936 if (dictionary == null)
937 throw new ObjectDisposedException (null);
938 if (dictionary.generation != stamp)
939 throw new InvalidOperationException ("out of sync");
942 void VerifyCurrent ()
944 VerifyState ();
945 if (next <= 0)
946 throw new InvalidOperationException ("Current is not valid");
949 public void Dispose ()
951 dictionary = null;
955 // This collection is a read only collection
956 [Serializable]
957 [DebuggerDisplay ("Count={Count}")]
958 [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
959 public sealed class KeyCollection : ICollection<TKey>, IEnumerable<TKey>, ICollection, IEnumerable {
960 Dictionary<TKey, TValue> dictionary;
962 public KeyCollection (Dictionary<TKey, TValue> dictionary)
964 if (dictionary == null)
965 throw new ArgumentNullException ("dictionary");
966 this.dictionary = dictionary;
970 public void CopyTo (TKey [] array, int index)
972 dictionary.CopyToCheck (array, index);
973 dictionary.Do_CopyTo<TKey, TKey> (array, index, pick_key);
976 public Enumerator GetEnumerator ()
978 return new Enumerator (dictionary);
981 void ICollection<TKey>.Add (TKey item)
983 throw new NotSupportedException ("this is a read-only collection");
986 void ICollection<TKey>.Clear ()
988 throw new NotSupportedException ("this is a read-only collection");
991 bool ICollection<TKey>.Contains (TKey item)
993 return dictionary.ContainsKey (item);
996 bool ICollection<TKey>.Remove (TKey item)
998 throw new NotSupportedException ("this is a read-only collection");
1001 IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator ()
1003 return this.GetEnumerator ();
1006 void ICollection.CopyTo (Array array, int index)
1008 var target = array as TKey [];
1009 if (target != null) {
1010 CopyTo (target, index);
1011 return;
1014 dictionary.CopyToCheck (array, index);
1015 dictionary.Do_ICollectionCopyTo<TKey> (array, index, pick_key);
1018 IEnumerator IEnumerable.GetEnumerator ()
1020 return this.GetEnumerator ();
1023 public int Count {
1024 get { return dictionary.Count; }
1027 bool ICollection<TKey>.IsReadOnly {
1028 get { return true; }
1031 bool ICollection.IsSynchronized {
1032 get { return false; }
1035 object ICollection.SyncRoot {
1036 get { return ((ICollection) dictionary).SyncRoot; }
1039 [Serializable]
1040 public struct Enumerator : IEnumerator<TKey>, IDisposable, IEnumerator {
1041 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1043 internal Enumerator (Dictionary<TKey, TValue> host)
1045 host_enumerator = host.GetEnumerator ();
1048 public void Dispose ()
1050 host_enumerator.Dispose ();
1053 public bool MoveNext ()
1055 return host_enumerator.MoveNext ();
1058 public TKey Current {
1059 get { return host_enumerator.current.Key; }
1062 object IEnumerator.Current {
1063 get { return host_enumerator.CurrentKey; }
1066 void IEnumerator.Reset ()
1068 host_enumerator.Reset ();
1073 // This collection is a read only collection
1074 [Serializable]
1075 [DebuggerDisplay ("Count={Count}")]
1076 [DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))]
1077 public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable {
1078 Dictionary<TKey, TValue> dictionary;
1080 public ValueCollection (Dictionary<TKey, TValue> dictionary)
1082 if (dictionary == null)
1083 throw new ArgumentNullException ("dictionary");
1084 this.dictionary = dictionary;
1087 public void CopyTo (TValue [] array, int index)
1089 dictionary.CopyToCheck (array, index);
1090 dictionary.Do_CopyTo<TValue, TValue> (array, index, pick_value);
1093 public Enumerator GetEnumerator ()
1095 return new Enumerator (dictionary);
1098 void ICollection<TValue>.Add (TValue item)
1100 throw new NotSupportedException ("this is a read-only collection");
1103 void ICollection<TValue>.Clear ()
1105 throw new NotSupportedException ("this is a read-only collection");
1108 bool ICollection<TValue>.Contains (TValue item)
1110 return dictionary.ContainsValue (item);
1113 bool ICollection<TValue>.Remove (TValue item)
1115 throw new NotSupportedException ("this is a read-only collection");
1118 IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator ()
1120 return this.GetEnumerator ();
1123 void ICollection.CopyTo (Array array, int index)
1125 var target = array as TValue [];
1126 if (target != null) {
1127 CopyTo (target, index);
1128 return;
1131 dictionary.CopyToCheck (array, index);
1132 dictionary.Do_ICollectionCopyTo<TValue> (array, index, pick_value);
1135 IEnumerator IEnumerable.GetEnumerator ()
1137 return this.GetEnumerator ();
1140 public int Count {
1141 get { return dictionary.Count; }
1144 bool ICollection<TValue>.IsReadOnly {
1145 get { return true; }
1148 bool ICollection.IsSynchronized {
1149 get { return false; }
1152 object ICollection.SyncRoot {
1153 get { return ((ICollection) dictionary).SyncRoot; }
1156 [Serializable]
1157 public struct Enumerator : IEnumerator<TValue>, IDisposable, IEnumerator {
1158 Dictionary<TKey, TValue>.Enumerator host_enumerator;
1160 internal Enumerator (Dictionary<TKey,TValue> host)
1162 host_enumerator = host.GetEnumerator ();
1165 public void Dispose ()
1167 host_enumerator.Dispose ();
1170 public bool MoveNext ()
1172 return host_enumerator.MoveNext ();
1175 public TValue Current {
1176 get { return host_enumerator.current.Value; }
1179 object IEnumerator.Current {
1180 get { return host_enumerator.CurrentValue; }
1183 void IEnumerator.Reset ()
1185 host_enumerator.Reset ();