2004-12-21 Ben Maurer <bmaurer@ximian.com>
[mono.git] / mcs / class / corlib / System.Collections / Hashtable.cs
1 //
2 // System.Collections.Hashtable.cs
3 //
4 // Author:
5 //   Sergey Chaban (serge@wildwestsoftware.com)
6 //
7
8 //
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Collections;
33 using System.Runtime.Serialization;
34
35 namespace System.Collections {
36
37         [Serializable]
38         public class Hashtable : IDictionary, ICollection, 
39                 IEnumerable, ICloneable, ISerializable, IDeserializationCallback
40         {
41
42                 [Serializable]
43                 internal struct Slot {
44                         internal Object key;
45
46                         internal Object value;
47
48                         // Hashcode. Chains are also marked through this.
49                         internal int hashMix;
50                 }
51
52                 [Serializable]
53                 internal class KeyMarker: IObjectReference
54                 {
55                         public readonly static KeyMarker Removed = new KeyMarker();
56                         public object GetRealObject (StreamingContext context)
57                         { return KeyMarker.Removed; }
58                 }
59
60                 //
61                 // Private data
62                 //
63
64                 const int CHAIN_MARKER  = ~Int32.MaxValue;
65
66
67                 private int inUse;
68                 private int modificationCount;
69                 private float loadFactor;
70                 private Slot [] table;
71                 private int threshold;
72         
73                 private HashKeys hashKeys;
74                 private HashValues hashValues;
75
76                 private IHashCodeProvider hcpRef;
77                 private IComparer comparerRef;
78                 private SerializationInfo serializationInfo;
79
80                 private static readonly int [] primeTbl = {
81                         11,
82                         19,
83                         37,
84                         73,
85                         109,
86                         163,
87                         251,
88                         367,
89                         557,
90                         823,
91                         1237,
92                         1861,
93                         2777,
94                         4177,
95                         6247,
96                         9371,
97                         14057,
98                         21089,
99                         31627,
100                         47431,
101                         71143,
102                         106721,
103                         160073,
104                         240101,
105                         360163,
106                         540217,
107                         810343,
108                         1215497,
109                         1823231,
110                         2734867,
111                         4102283,
112                         6153409,
113                         9230113,
114                         13845163
115                 };
116
117                 //
118                 // Constructors
119                 //
120
121                 public Hashtable () : this (0, 1.0f) {}
122
123
124                 public Hashtable (int capacity, float loadFactor, IHashCodeProvider hcp, IComparer comparer) {
125                         if (capacity<0)
126                                 throw new ArgumentOutOfRangeException ("capacity", "negative capacity");
127
128                         if (loadFactor < 0.1f || loadFactor > 1.0f || Single.IsNaN (loadFactor))
129                                 throw new ArgumentOutOfRangeException ("loadFactor", "load factor");
130
131                         if (capacity == 0) ++capacity;
132                         this.loadFactor = 0.75f*loadFactor;
133                         double tableSize = capacity / this.loadFactor;
134
135                         if (tableSize > Int32.MaxValue)
136                                 throw new ArgumentException ("Size is too big");
137
138                         int size = (int) tableSize;
139                         size = ToPrime (size);
140                         this.SetTable (new Slot [size]);
141
142                         this.hcp = hcp;
143                         this.comparer = comparer;
144
145                         this.inUse = 0;
146                         this.modificationCount = 0;
147
148                 }
149
150                 public Hashtable (int capacity, float loadFactor) :
151                         this (capacity, loadFactor, null, null)
152                 {
153                 }
154
155                 public Hashtable (int capacity) : this (capacity, 1.0f)
156                 {
157                 }
158
159                 public Hashtable (int capacity,
160                                   IHashCodeProvider hcp,
161                                   IComparer comparer)
162                         : this (capacity, 1.0f, hcp, comparer)
163                 {
164                 }
165
166
167                 public Hashtable (IDictionary d, float loadFactor,
168                                   IHashCodeProvider hcp, IComparer comparer)
169                         : this (d!=null ? d.Count : 0,
170                                 loadFactor, hcp, comparer)
171                 {
172
173                         if (d  ==  null)
174                                 throw new ArgumentNullException ("dictionary");
175
176                         IDictionaryEnumerator it = d.GetEnumerator ();
177                         while (it.MoveNext ()) {
178                                 Add (it.Key, it.Value);
179                         }
180
181                 }
182
183                 public Hashtable (IDictionary d, float loadFactor)
184                        : this (d, loadFactor, null, null)
185                 {
186                 }
187
188
189                 public Hashtable (IDictionary d) : this (d, 1.0f)
190                 {
191                 }
192
193                 public Hashtable (IDictionary d, IHashCodeProvider hcp, IComparer comparer)
194                                  : this (d, 1.0f, hcp, comparer)
195                 {
196                 }
197
198                 public Hashtable (IHashCodeProvider hcp, IComparer comparer)
199                                  : this (1, 1.0f, hcp, comparer)
200                 {
201                 }
202
203                 protected Hashtable (SerializationInfo info, StreamingContext context)
204                 {
205                         serializationInfo = info;
206                 }
207
208                 //
209                 // Properties
210                 //
211
212                 protected IComparer comparer {
213                         set {
214                                 comparerRef = value;
215                         }
216                         get {
217                                 return comparerRef;
218                         }
219                 }
220
221                 protected IHashCodeProvider hcp {
222                         set {
223                                 hcpRef = value;
224                         }
225                         get {
226                                 return hcpRef;
227                         }
228                 }
229
230                 // ICollection
231
232                 public virtual int Count {
233                         get {
234                                 return inUse;
235                         }
236                 }
237
238                 public virtual bool IsSynchronized {
239                         get {
240                                 return false;
241                         }
242                 }
243
244                 public virtual Object SyncRoot {
245                         get {
246                                 return this;
247                         }
248                 }
249
250
251
252                 // IDictionary
253
254                 public virtual bool IsFixedSize {
255                         get {
256                                 return false;
257                         }
258                 }
259
260
261                 public virtual bool IsReadOnly {
262                         get {
263                                 return false;
264                         }
265                 }
266
267                 public virtual ICollection Keys {
268                         get {
269                                 if (this.hashKeys == null)
270                                         this.hashKeys = new HashKeys (this);
271                                 return this.hashKeys;
272                         }
273                 }
274
275                 public virtual ICollection Values {
276                         get {
277                                 if (this.hashValues == null)
278                                         this.hashValues = new HashValues (this);
279                                 return this.hashValues;
280                         }
281                 }
282
283
284
285                 public virtual Object this [Object key] {
286                         get {
287                                 int i = Find (key);
288         
289                                 if (i >= 0)
290                                         return table [i].value;
291                                 
292                                 return null;
293                         }
294                         set {
295                                 PutImpl (key, value, true);
296                         }
297                 }
298
299
300
301
302                 //
303                 // Interface methods
304                 //
305
306
307                 // IEnumerable
308
309                 IEnumerator IEnumerable.GetEnumerator ()
310                 {
311                         return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
312                 }
313
314
315                 // ICollection
316                 public virtual void CopyTo (Array array, int arrayIndex)
317                 {
318                         if (null == array)
319                                 throw new ArgumentNullException ("array");
320
321                         if (arrayIndex < 0)
322                                 throw new ArgumentOutOfRangeException ("arrayIndex");
323
324                         if (array.Rank > 1)
325                                 throw new ArgumentException ("array is multidimensional");
326
327                         if ((array.Length > 0) && (arrayIndex >= array.Length))
328                                 throw new ArgumentException ("arrayIndex is equal to or greater than array.Length");
329
330                         if (arrayIndex + this.inUse > array.Length)
331                                 throw new ArgumentException ("Not enough room from arrayIndex to end of array for this Hashtable");
332
333                         IDictionaryEnumerator it = GetEnumerator ();
334                         int i = arrayIndex;
335
336                         while (it.MoveNext ()) {
337                                 array.SetValue (it.Entry, i++);
338                         }
339                 }
340
341
342                 // IDictionary
343
344                 public virtual void Add (Object key, Object value)
345                 {
346                         PutImpl (key, value, false);
347                 }
348
349                 public virtual void Clear ()
350                 {
351                         for (int i = 0;i<table.Length;i++) {
352                                 table [i].key = null;
353                                 table [i].value = null;
354                                 table [i].hashMix = 0;
355                         }
356
357                         inUse = 0;
358                         modificationCount++;
359                 }
360
361                 public virtual bool Contains (Object key)
362                 {
363                         return (Find (key) >= 0);
364                 }
365
366                 public virtual IDictionaryEnumerator GetEnumerator ()
367                 {
368                         return new Enumerator (this, EnumeratorMode.ENTRY_MODE);
369                 }
370
371                 public virtual void Remove (Object key)
372                 {
373                         int i = Find (key);
374                         if (i >= 0) {
375                                 Slot [] table = this.table;
376                                 int h = table [i].hashMix;
377                                 h &= CHAIN_MARKER;
378                                 table [i].hashMix = h;
379                                 table [i].key = (h != 0)
380                                               ? KeyMarker.Removed
381                                               : null;
382                                 table [i].value = null;
383                                 --inUse;
384                                 ++modificationCount;
385                         }
386                 }
387
388                 public virtual bool ContainsKey (object key)
389                 {
390                         return Contains (key);
391                 }
392
393                 public virtual bool ContainsValue (object value)
394                 {
395                         int size = this.table.Length;
396                         Slot [] table = this.table;
397                         if (value == null) {
398                                 for (int i = 0; i < size; i++) {
399                                         Slot entry = table [i];
400                                         if (entry.key != null && entry.key!= KeyMarker.Removed
401                                         && entry.value == null) {
402                                                 return true;
403                                         }
404                                 }
405                         } else { 
406                                 for (int i = 0; i < size; i++) {
407                                         Slot entry = table [i];
408                                         if (entry.key != null && entry.key!= KeyMarker.Removed
409                                         && value.Equals (entry.value)) {
410                                                 return true;
411                                         }
412                                 }
413                         }
414                         return false;
415                 }
416
417
418                 // ICloneable
419
420                 public virtual object Clone ()
421                 {
422                         Hashtable ht = new Hashtable (Count, hcp, comparer);
423                         ht.inUse = 0;
424                         ht.loadFactor = this.loadFactor;
425
426                         // FIXME: maybe it's faster to simply
427                         //        copy the back-end array?
428
429                         IDictionaryEnumerator it = GetEnumerator ();
430                         while (it.MoveNext ()) {
431                                 ht [it.Key] = it.Value;
432                         }
433
434                         return ht;
435                 }
436
437                 public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
438                 {
439                         if (info == null)
440                                 throw new ArgumentNullException ("info");
441
442                         info.AddValue ("LoadFactor", loadFactor);
443                         info.AddValue ("Version", modificationCount);
444                         info.AddValue ("Comparer", comparerRef);
445                         info.AddValue ("HashCodeProvider", hcpRef);
446                         info.AddValue ("HashSize", this.table.Length);
447 // Create Keys
448                         Object [] keys = new Object[inUse];
449                         CopyToArray(keys, 0, EnumeratorMode.KEY_MODE); 
450   
451 // Create Values
452                         Object [] values = new Object[inUse];
453                         CopyToArray(values, 0, EnumeratorMode.VALUE_MODE);
454
455                         info.AddValue ("Keys", keys);
456                         info.AddValue ("Values", values);
457
458                 }
459
460                 public virtual void OnDeserialization (object sender)
461                 {
462                         if (serializationInfo == null) return;
463
464                         loadFactor = (float) serializationInfo.GetValue ("LoadFactor", typeof(float));
465                         modificationCount = (int) serializationInfo.GetValue ("Version", typeof(int));
466                         comparerRef = (IComparer) serializationInfo.GetValue ("Comparer", typeof (object));
467                         hcpRef = (IHashCodeProvider) serializationInfo.GetValue ("HashCodeProvider", typeof (object));
468                         int size = (int) serializationInfo.GetValue ("HashSize", typeof(int));
469                         
470                         Object [] keys = (Object []) serializationInfo.GetValue("Keys", typeof(Object [] ));
471                         Object [] values = (Object []) serializationInfo.GetValue("Values", typeof(Object [] ));
472
473                         if (keys.Length != values.Length) 
474                           throw new SerializationException("Keys and values of uneven size");
475                          
476                         size = ToPrime (size);
477                         this.SetTable (new Slot [size]);
478                         
479                         for(int i=0;i<keys.Length;i++)
480                                 Add(keys[i], values[i]);
481                 
482                         AdjustThreshold();
483                         
484                         serializationInfo = null;
485                 }
486
487                 /// <summary>
488                 ///  Returns a synchronized (thread-safe)
489                 ///  wrapper for the Hashtable.
490                 /// </summary>
491                 public static Hashtable Synchronized (Hashtable table)
492                 {
493                         if (table == null)
494                                 throw new ArgumentNullException("table");
495                         return new SyncHashtable (table);
496                 }
497
498
499
500                 //
501                 // Protected instance methods
502                 //
503
504                 /// <summary>Returns the hash code for the specified key.</summary>
505                 protected virtual int GetHash (Object key)
506                 {
507                         if (hcpRef == null)
508                                 return key.GetHashCode ();
509                         
510                         return hcpRef.GetHashCode (key);
511                 }
512
513                 /// <summary>
514                 ///  Compares a specific Object with a specific key
515                 ///  in the Hashtable.
516                 /// </summary>
517                 protected virtual bool KeyEquals (Object item, Object key)
518                 {
519                         if (comparerRef == null)
520                                 return item.Equals (key);
521                         
522                         return comparerRef.Compare (item, key) == 0;
523                 }
524
525
526
527                 //
528                 // Private instance methods
529                 //
530
531                 private void AdjustThreshold ()
532                 {
533                         int size = table.Length;
534
535                         threshold = (int) (size*loadFactor);
536                         if (this.threshold >= size)
537                                 threshold = size-1;
538                 }
539
540                 private void SetTable (Slot [] table)
541                 {
542                         if (table == null)
543                                 throw new ArgumentNullException ("table");
544
545                         this.table = table;
546                         AdjustThreshold ();
547                 }
548
549                 private int Find (Object key)
550                 {
551                         if (key == null)
552                                 throw new ArgumentNullException ("key", "null key");
553
554                         Slot [] table = this.table;
555                         uint size = (uint) table.Length;
556                         int h = this.GetHash (key) & Int32.MaxValue;
557                         uint indx = (uint)h;
558                         uint step = (uint) ((h >> 5)+1) % (size-1)+1;
559                         
560
561                         for (uint i = size; i > 0; i--) {
562                                 indx %= size;
563                                 Slot entry = table [indx];
564                                 Object k = entry.key;
565                                 if (k == null)
566                                         return -1;
567                                 if (k == key || ((entry.hashMix & Int32.MaxValue) == h
568                                     && this.KeyEquals (key, k))) {
569                                         return (int) indx;
570                                 }
571
572                                 if ((entry.hashMix & CHAIN_MARKER) == 0)
573                                         return -1;
574
575                                 indx += step;
576                         }
577                         return -1;
578                 }
579
580
581                 private void Rehash ()
582                 {
583                         int oldSize = this.table.Length;
584
585                         // From the SDK docs:
586                         //   Hashtable is automatically increased
587                         //   to the smallest prime number that is larger
588                         //   than twice the current number of Hashtable buckets
589                         uint newSize = (uint)ToPrime ((oldSize<<1)|1);
590
591
592                         Slot [] newTable = new Slot [newSize];
593                         Slot [] table = this.table;
594
595                         for (int i = 0;i<oldSize;i++) {
596                                 Slot s = table [i];
597                                 if (s.key!= null) {
598                                         int h = s.hashMix & Int32.MaxValue;
599                                         uint spot = (uint)h;
600                                         uint step = ((uint) (h>>5)+1)% (newSize-1)+1;
601                                         for (uint j = spot%newSize;;spot+= step, j = spot%newSize) {
602                                                 // No check for KeyMarker.Removed here,
603                                                 // because the table is just allocated.
604                                                 if (newTable [j].key == null) {
605                                                         newTable [j].key = s.key;
606                                                         newTable [j].value = s.value;
607                                                         newTable [j].hashMix |= h;
608                                                         break;
609                                                 } else {
610                                                         newTable [j].hashMix |= CHAIN_MARKER;
611                                                 }
612                                         }
613                                 }
614                         }
615
616                         ++this.modificationCount;
617
618                         this.SetTable (newTable);
619                 }
620
621
622                 private void PutImpl (Object key, Object value, bool overwrite)
623                 {
624                         if (key == null)
625                                 throw new ArgumentNullException ("key", "null key");
626
627                         uint size = (uint)this.table.Length;
628                         if (this.inUse >= this.threshold) {
629                                 this.Rehash ();
630                                 size = (uint)this.table.Length;
631                         }
632
633                         int h = this.GetHash (key) & Int32.MaxValue;
634                         uint spot = (uint)h;
635                         uint step = (uint) ((spot>>5)+1)% (size-1)+1;
636                         Slot [] table = this.table;
637                         Slot entry;
638
639                         int freeIndx = -1;
640                         for (int i = 0; i < size; i++) {
641                                 int indx = (int) (spot % size);
642                                 entry = table [indx];
643
644                                 if (freeIndx == -1
645                                     && entry.key == KeyMarker.Removed
646                                     && (entry.hashMix & CHAIN_MARKER) != 0)
647                                         freeIndx = indx;
648
649                                 if (entry.key == null ||
650                                     (entry.key == KeyMarker.Removed
651                                      && (entry.hashMix & CHAIN_MARKER) == 0)) {
652
653                                         if (freeIndx == -1)
654                                                 freeIndx = indx;
655                                         break;
656                                 }
657
658                                 if ((entry.hashMix & Int32.MaxValue) == h && KeyEquals (key, entry.key)) {
659                                         if (overwrite) {
660                                                 table [indx].value = value;
661                                                 ++this.modificationCount;
662                                         } else {
663                                                 // Handle Add ():
664                                                 // An entry with the same key already exists in the Hashtable.
665                                                 throw new ArgumentException (
666                                                                 "Key duplication when adding: " + key);
667                                         }
668                                         return;
669                                 }
670
671                                 if (freeIndx == -1) {
672                                         table [indx].hashMix |= CHAIN_MARKER;
673                                 }
674
675                                 spot+= step;
676
677                         }
678
679                         if (freeIndx!= -1) {
680                                 table [freeIndx].key = key;
681                                 table [freeIndx].value = value;
682                                 table [freeIndx].hashMix |= h;
683
684                                 ++this.inUse;
685                                 ++this.modificationCount;
686                         }
687
688                 }
689
690                 private void  CopyToArray (Array arr, int i,
691                                            EnumeratorMode mode)
692                 {
693                         IEnumerator it = new Enumerator (this, mode);
694
695                         while (it.MoveNext ()) {
696                                 arr.SetValue (it.Current, i++);
697                         }
698                 }
699
700
701
702                 //
703                 // Private static methods
704                 //
705                 private static bool TestPrime (int x)
706                 {
707                         if ((x & 1) != 0) {
708                                 for (int n = 3; n< (int)Math.Sqrt (x); n += 2) {
709                                         if ((x % n) == 0)
710                                                 return false;
711                                 }
712                                 return true;
713                         }
714                         // There is only one even prime - 2.
715                         return (x == 2);
716                 }
717
718                 private static int CalcPrime (int x)
719                 {
720                         for (int i = (x & (~1))-1; i< Int32.MaxValue; i += 2) {
721                                 if (TestPrime (i)) return i;
722                         }
723                         return x;
724                 }
725
726                 private static int ToPrime (int x)
727                 {
728                         for (int i = 0; i < primeTbl.Length; i++) {
729                                 if (x <= primeTbl [i])
730                                         return primeTbl [i];
731                         }
732                         return CalcPrime (x);
733                 }
734
735
736
737
738                 //
739                 // Inner classes
740                 //
741
742                 private enum EnumeratorMode : int {KEY_MODE = 0, VALUE_MODE, ENTRY_MODE};
743
744                 private sealed class Enumerator : IDictionaryEnumerator, IEnumerator {
745
746                         private Hashtable host;
747                         private int stamp;
748                         private int pos;
749                         private int size;
750                         private EnumeratorMode mode;
751
752                         private Object currentKey;
753                         private Object currentValue;
754
755                         private readonly static string xstr = "Hashtable.Enumerator: snapshot out of sync.";
756
757                         public Enumerator (Hashtable host, EnumeratorMode mode) {
758                                 this.host = host;
759                                 stamp = host.modificationCount;
760                                 size = host.table.Length;
761                                 this.mode = mode;
762                                 Reset ();
763                         }
764
765                         public Enumerator (Hashtable host)
766                                    : this (host, EnumeratorMode.KEY_MODE) {}
767
768
769                         private void FailFast ()
770                         {
771                                 if (host.modificationCount != stamp) {
772                                         throw new InvalidOperationException (xstr);
773                                 }
774                         }
775
776                         public void Reset ()
777                         {
778                                 FailFast ();
779
780                                 pos = -1;
781                                 currentKey = null;
782                                 currentValue = null;
783                         }
784
785                         public bool MoveNext ()
786                         {
787                                 FailFast ();
788
789                                 if (pos < size) {
790                                         while (++pos < size) {
791                                                 Slot entry = host.table [pos];
792
793                                                 if (entry.key != null && entry.key != KeyMarker.Removed) {
794                                                         currentKey = entry.key;
795                                                         currentValue = entry.value;
796                                                         return true;
797                                                 }
798                                         }
799                                 }
800
801                                 currentKey = null;
802                                 currentValue = null;
803                                 return false;
804                         }
805
806                         public DictionaryEntry Entry
807                         {
808                                 get {
809                                         if (currentKey == null) throw new InvalidOperationException ();
810                                         FailFast ();
811                                         return new DictionaryEntry (currentKey, currentValue);
812                                 }
813                         }
814
815                         public Object Key {
816                                 get {
817                                         if (currentKey == null) throw new InvalidOperationException ();
818                                         FailFast ();
819                                         return currentKey;
820                                 }
821                         }
822
823                         public Object Value {
824                                 get {
825                                         if (currentKey == null) throw new InvalidOperationException ();
826                                         FailFast ();
827                                         return currentValue;
828                                 }
829                         }
830
831                         public Object Current {
832                                 get {
833                                         if (currentKey == null) throw new InvalidOperationException ();
834                                                 
835                                         switch (mode) {
836                                         case EnumeratorMode.KEY_MODE:
837                                                 return currentKey;
838                                         case EnumeratorMode.VALUE_MODE:
839                                                 return currentValue;
840                                         case EnumeratorMode.ENTRY_MODE:
841                                                 return new DictionaryEntry (currentKey, currentValue);
842                                         }
843                                         throw new Exception ("should never happen");
844                                 }
845                         }
846                 }
847
848
849
850                 private class HashKeys : ICollection, IEnumerable {
851
852                         private Hashtable host;
853
854                         public HashKeys (Hashtable host) {
855                                 if (host == null)
856                                         throw new ArgumentNullException ();
857
858                                 this.host = host;
859                         }
860
861                         // ICollection
862
863                         public virtual int Count {
864                                 get {
865                                         return host.Count;
866                                 }
867                         }
868
869                         public virtual bool IsSynchronized {
870                                 get {
871                                         return host.IsSynchronized;
872                                 }
873                         }
874
875                         public virtual Object SyncRoot {
876                                 get {return host.SyncRoot;}
877                         }
878
879                         public virtual void CopyTo (Array array, int arrayIndex)
880                         {
881                                 if (array == null)
882                                         throw new ArgumentNullException ("array");
883                                 if (array.Rank != 1)
884                                         throw new ArgumentException ("array");
885                                 if (arrayIndex < 0) 
886                                         throw new ArgumentOutOfRangeException ("arrayIndex");
887                                 if (array.Length - arrayIndex < Count)
888                                         throw new ArgumentException ("not enough space");
889                                 
890                                 host.CopyToArray (array, arrayIndex, EnumeratorMode.KEY_MODE);
891                         }
892
893                         // IEnumerable
894
895                         public virtual IEnumerator GetEnumerator ()
896                         {
897                                 return new Hashtable.Enumerator (host, EnumeratorMode.KEY_MODE);
898                         }
899                 }
900
901
902                 private class HashValues : ICollection, IEnumerable {
903
904                         private Hashtable host;
905
906                         public HashValues (Hashtable host) {
907                                 if (host == null)
908                                         throw new ArgumentNullException ();
909
910                                 this.host = host;
911                         }
912
913                         // ICollection
914
915                         public virtual int Count {
916                                 get {
917                                         return host.Count;
918                                 }
919                         }
920
921                         public virtual bool IsSynchronized {
922                                 get {
923                                         return host.IsSynchronized;
924                                 }
925                         }
926
927                         public virtual Object SyncRoot {
928                                 get {
929                                         return host.SyncRoot;
930                                 }
931                         }
932
933                         public virtual void CopyTo (Array array, int arrayIndex)
934                         {
935                                 if (array == null)
936                                         throw new ArgumentNullException ("array");
937                                 if (array.Rank != 1)
938                                         throw new ArgumentException ("array");
939                                 if (arrayIndex < 0) 
940                                         throw new ArgumentOutOfRangeException ("arrayIndex");
941                                 if (array.Length - arrayIndex < Count)
942                                         throw new ArgumentException ("not enough space");
943                                 
944                                 host.CopyToArray (array, arrayIndex, EnumeratorMode.VALUE_MODE);
945                         }
946
947                         // IEnumerable
948
949                         public virtual IEnumerator GetEnumerator ()
950                         {
951                                 return new Hashtable.Enumerator (host, EnumeratorMode.VALUE_MODE);
952                         }
953                 }
954
955
956                 [Serializable]
957                 private class SyncHashtable : Hashtable, IEnumerable {
958
959                         private Hashtable host;
960
961                         public SyncHashtable (Hashtable host) {
962                                 if (host == null)
963                                         throw new ArgumentNullException ();
964
965                                 this.host = host;
966                         }
967
968                         internal SyncHashtable (SerializationInfo info, StreamingContext context)
969                         {
970                                 host = (Hashtable) info.GetValue("ParentTable", typeof(Hashtable));
971                         }
972                         
973                         public override void GetObjectData (SerializationInfo info, StreamingContext context)
974                         {
975                                 info.AddValue ("ParentTable", host);
976                         }
977                         
978                         // ICollection
979
980                         public override int Count {
981                                 get {
982                                         return host.Count;
983                                 }
984                         }
985
986                         public override bool IsSynchronized {
987                                 get {
988                                         return true;
989                                 }
990                         }
991
992                         public override Object SyncRoot {
993                                 get {
994                                         return host.SyncRoot;
995                                 }
996                         }
997
998
999
1000                         // IDictionary
1001
1002                         public override bool IsFixedSize {
1003                                 get {
1004                                         return host.IsFixedSize;
1005                                 }     
1006                         }
1007
1008
1009                         public override bool IsReadOnly {
1010                                 get {
1011                                         return host.IsReadOnly;
1012                                 }
1013                         }
1014
1015                         public override ICollection Keys {
1016                                 get {
1017                                         ICollection keys = null;
1018                                         lock (host.SyncRoot) {
1019                                                 keys = host.Keys;
1020                                         }
1021                                         return keys;
1022                                 }
1023                         }
1024
1025                         public override ICollection Values {
1026                                 get {
1027                                         ICollection vals = null;
1028                                         lock (host.SyncRoot) {
1029                                                 vals = host.Values;
1030                                         }
1031                                         return vals;
1032                                 }
1033                         }
1034
1035
1036
1037                         public override Object this [Object key] {
1038                                 get {
1039                                         return host [key];
1040                                 }
1041                                 set {
1042                                         lock (host.SyncRoot) {
1043                                                 host [key] = value;
1044                                         }
1045                                 }
1046                         }
1047
1048                         // IEnumerable
1049
1050                         IEnumerator IEnumerable.GetEnumerator ()
1051                         {
1052                                 return new Enumerator (host, EnumeratorMode.KEY_MODE);
1053                         }
1054
1055
1056
1057
1058                         // ICollection
1059
1060                         public override void CopyTo (Array array, int arrayIndex)
1061                         {
1062                                 host.CopyTo (array, arrayIndex);
1063                         }
1064
1065
1066                         // IDictionary
1067
1068                         public override void Add (Object key, Object value)
1069                         {
1070                                 lock (host.SyncRoot) {
1071                                         host.Add (key, value);
1072                                 }
1073                         }
1074
1075                         public override void Clear () 
1076                         {
1077                                 lock (host.SyncRoot) {
1078                                         host.Clear ();
1079                                 }
1080                         }
1081
1082                         public override bool Contains (Object key)
1083                         {
1084                                 return (host.Find (key) >= 0);
1085                         }
1086
1087                         public override IDictionaryEnumerator GetEnumerator ()
1088                         {
1089                                 return new Enumerator (host, EnumeratorMode.ENTRY_MODE);
1090                         }
1091
1092                         public override void Remove (Object key)
1093                         {
1094                                 lock (host.SyncRoot) {
1095                                         host.Remove (key);
1096                                 }
1097                         }
1098
1099
1100
1101                         public override bool ContainsKey (object key)
1102                         {
1103                                 return host.Contains (key);
1104                         }
1105
1106                         public override bool ContainsValue (object value)
1107                         {
1108                                 return host.ContainsValue (value);
1109                         }
1110
1111
1112                         // ICloneable
1113
1114                         public override object Clone ()
1115                         {
1116                                 lock(host.SyncRoot) {
1117                                         return new SyncHashtable( (Hashtable) host.Clone () );
1118                                 }
1119                         }
1120
1121                 } // SyncHashtable
1122
1123
1124         } // Hashtable
1125
1126 }