Hashtable.cs 23 KB

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