Hashtable.cs 26 KB

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