Hashtable.cs 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  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. lock(host.SyncRoot) {
  873. return new SyncHashtable( (Hashtable) host.Clone () );
  874. }
  875. }
  876. } // SyncHashtable
  877. } // Hashtable
  878. }