Hashtable.cs 21 KB

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