PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/CommonDictionaryStorage.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 738 lines | 493 code | 118 blank | 127 comment | 141 complexity | 8bc552d99df48d26133098e23f76ec98 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Runtime.CompilerServices;
  20. using System.Runtime.Serialization;
  21. using Microsoft.Scripting.Generation;
  22. using Microsoft.Scripting.Utils;
  23. using IronPython.Runtime.Operations;
  24. using IronPython.Runtime.Types;
  25. namespace IronPython.Runtime {
  26. /// <summary>
  27. /// General purpose storage used for most PythonDictionarys.
  28. ///
  29. /// This dictionary storage is thread safe for multiple readers or writers.
  30. ///
  31. /// Mutations to the dictionary involves a simple locking strategy of
  32. /// locking on the DictionaryStorage object to ensure that only one
  33. /// mutation happens at a time.
  34. ///
  35. /// Reads against the dictionary happen lock free. When the dictionary is mutated
  36. /// it is either adding or removing buckets in a thread-safe manner so that the readers
  37. /// will either see a consistent picture as if the read occured before or after the mutation.
  38. ///
  39. /// When resizing the dictionary the buckets are replaced atomically so that the reader
  40. /// sees the new buckets or the old buckets. When reading the reader first reads
  41. /// the buckets and then calls a static helper function to do the read from the bucket
  42. /// array to ensure that readers are not seeing multiple bucket arrays.
  43. /// </summary>
  44. [Serializable]
  45. internal sealed class CommonDictionaryStorage : DictionaryStorage
  46. #if !SILVERLIGHT
  47. , ISerializable, IDeserializationCallback
  48. #endif
  49. {
  50. private Bucket[] _buckets;
  51. private int _count;
  52. private Func<object, int> _hashFunc;
  53. private Func<object, object, bool> _eqFunc;
  54. private Type _keyType;
  55. private int _version;
  56. private const int InitialBucketSize = 7;
  57. private const int ResizeMultiplier = 3;
  58. // pre-created delegate instances shared by all homogeneous dictionaries for primitive types.
  59. private static readonly Func<object, int> _primitiveHash = PrimitiveHash, _doubleHash = DoubleHash, _intHash = IntHash, _tupleHash = TupleHash, _genericHash = GenericHash;
  60. private static readonly Func<object, object, bool> _intEquals = IntEquals, _doubleEquals = DoubleEquals, _stringEquals = StringEquals, _tupleEquals = TupleEquals, _genericEquals = GenericEquals, _objectEq = System.Object.ReferenceEquals;
  61. // marker type used to indicate we've gone megamorphic
  62. private static readonly Type HeterogeneousType = typeof(CommonDictionaryStorage); // a type we can never see here.
  63. /// <summary>
  64. /// Creates a new dictionary storage with no buckets
  65. /// </summary>
  66. public CommonDictionaryStorage() {
  67. }
  68. /// <summary>
  69. /// Creates a new dictionary storage with no buckets
  70. /// </summary>
  71. public CommonDictionaryStorage(int count) {
  72. _buckets = new Bucket[count + 1];
  73. }
  74. /// <summary>
  75. /// Creates a new dictionary geting values/keys from the
  76. /// items arary
  77. /// </summary>
  78. public CommonDictionaryStorage(object[] items, bool isHomogeneous)
  79. : this(Math.Max(items.Length / 2, InitialBucketSize)) {
  80. // always called w/ items, and items should be even (key/value pairs)
  81. Debug.Assert(items.Length > 0 && (items.Length & 0x01) == 0);
  82. PythonType t = DynamicHelpers.GetPythonType(items[1]);
  83. if (!isHomogeneous) {
  84. for (int i = 1; i < items.Length / 2; i++) {
  85. if (DynamicHelpers.GetPythonType(items[i * 2 + 1]) != t) {
  86. SetHeterogeneousSites();
  87. t = null;
  88. break;
  89. }
  90. }
  91. }
  92. if (t != null) {
  93. // homogeneous collection
  94. UpdateHelperFunctions(t, items[1]);
  95. }
  96. for (int i = 0; i < items.Length / 2; i++) {
  97. AddOne(items[i * 2 + 1], items[i * 2]);
  98. }
  99. }
  100. public int Version {
  101. get{
  102. return _version;
  103. }
  104. }
  105. private void AddItems(object[] items) {
  106. for (int i = 0; i < items.Length / 2; i++) {
  107. AddNoLock(items[i * 2 + 1], items[i * 2]);
  108. }
  109. }
  110. /// <summary>
  111. /// Creates a new dictionary storage with the given set of buckets
  112. /// and size. Used when cloning the dictionary storage.
  113. /// </summary>
  114. private CommonDictionaryStorage(Bucket[] buckets, int count, Type keyType, Func<object, int> hashFunc, Func<object, object, bool> eqFunc) {
  115. _buckets = buckets;
  116. _count = count;
  117. _keyType = keyType;
  118. _hashFunc = hashFunc;
  119. _eqFunc = eqFunc;
  120. }
  121. #if !SILVERLIGHT
  122. private CommonDictionaryStorage(SerializationInfo info, StreamingContext context) {
  123. // remember the serialization info, we'll deserialize when we get the callback. This
  124. // enables special types like DBNull.Value to successfully be deserialized inside of us. We
  125. // store the serialization info in a special bucket so we don't have an extra field just for
  126. // serialization
  127. _buckets = new Bucket[] { new DeserializationBucket(info) };
  128. }
  129. #endif
  130. /// <summary>
  131. /// Adds a new item to the dictionary, replacing an existing one if it already exists.
  132. /// </summary>
  133. public override void Add(object key, object value) {
  134. lock (this) {
  135. AddNoLock(key, value);
  136. }
  137. }
  138. public override void AddNoLock(object key, object value) {
  139. if (_buckets == null) {
  140. Initialize();
  141. }
  142. Type t = CompilerHelpers.GetType(key);
  143. if (t != _keyType && _keyType != HeterogeneousType) {
  144. UpdateHelperFunctions(t, key);
  145. }
  146. AddOne(key, value);
  147. }
  148. private void AddOne(object key, object value) {
  149. if (Add(_buckets, key, value)) {
  150. _count++;
  151. if (_count >= _buckets.Length) {
  152. // grow the hash table
  153. EnsureSize(_buckets.Length * ResizeMultiplier);
  154. }
  155. }
  156. }
  157. private void UpdateHelperFunctions(Type t, object key) {
  158. if (_keyType == null) {
  159. // first time through, get the sites for this specific type...
  160. if (t == typeof(int)) {
  161. _hashFunc = _intHash;
  162. _eqFunc = _intEquals;
  163. } else if (t == typeof(string)) {
  164. _hashFunc = _primitiveHash;
  165. _eqFunc = _stringEquals;
  166. } else if (t == typeof(double)) {
  167. _hashFunc = _doubleHash;
  168. _eqFunc = _doubleEquals;
  169. } else if (t == typeof(PythonTuple)) {
  170. _hashFunc = _tupleHash;
  171. _eqFunc = _tupleEquals;
  172. } else if(t == typeof(Type).GetType()) { // this odd check checks for RuntimeType.
  173. _hashFunc = _primitiveHash;
  174. _eqFunc = _objectEq;
  175. } else {
  176. // random type, but still homogeneous... get a shared site for this type.
  177. PythonType pt = DynamicHelpers.GetPythonType(key);
  178. var hashSite = PythonContext.GetHashSite(pt);
  179. var equalSite = DefaultContext.DefaultPythonContext.GetEqualSite(pt);
  180. AssignSiteDelegates(hashSite, equalSite);
  181. }
  182. _keyType = t;
  183. } else if (_keyType != HeterogeneousType) {
  184. // 2nd time through, we're adding a new type so we have mutliple types now,
  185. // make a new site for this storage
  186. SetHeterogeneousSites();
  187. // we need to clone the buckets so any lock-free readers will only see
  188. // the old buckets which are homogeneous
  189. _buckets = (Bucket[])_buckets.Clone();
  190. }
  191. // else we have already created a new site this dictionary
  192. }
  193. private void SetHeterogeneousSites() {
  194. var hashSite = DefaultContext.DefaultPythonContext.MakeHashSite();
  195. var equalSite = DefaultContext.DefaultPythonContext.MakeEqualSite();
  196. AssignSiteDelegates(hashSite, equalSite);
  197. _keyType = HeterogeneousType;
  198. }
  199. private void AssignSiteDelegates(CallSite<Func<CallSite, object, int>> hashSite, CallSite<Func<CallSite, object, object, bool>> equalSite) {
  200. _hashFunc = (o) => hashSite.Target(hashSite, o);
  201. _eqFunc = (o1, o2) => equalSite.Target(equalSite, o1, o2);
  202. }
  203. private void EnsureSize(int newSize) {
  204. if (_buckets.Length >= newSize) {
  205. return;
  206. }
  207. Bucket[] newBuckets = new Bucket[newSize];
  208. for (int i = 0; i < _buckets.Length; i++) {
  209. Bucket curBucket = _buckets[i];
  210. while (curBucket != null) {
  211. Bucket next = curBucket.Next;
  212. AddWorker(newBuckets, curBucket.Key, curBucket.Value, curBucket.HashCode);
  213. curBucket = next;
  214. }
  215. }
  216. _buckets = newBuckets;
  217. }
  218. /// <summary>
  219. /// Initializes the buckets to their initial capacity, the caller
  220. /// must check if the buckets are empty first.
  221. /// </summary>
  222. private void Initialize() {
  223. _buckets = new Bucket[InitialBucketSize];
  224. }
  225. /// <summary>
  226. /// Static add helper that works over a single set of buckets. Used for
  227. /// both the normal add case as well as the resize case.
  228. /// </summary>
  229. private bool Add(Bucket[] buckets, object key, object value) {
  230. int hc = Hash(key);
  231. return AddWorker(buckets, key, value, hc);
  232. }
  233. private bool AddWorker(Bucket[] buckets, object key, object value, int hc) {
  234. _version++;
  235. int index = hc % buckets.Length;
  236. Bucket prev = buckets[index];
  237. Bucket cur = prev;
  238. while (cur != null) {
  239. if (cur.HashCode == hc && _eqFunc(key, cur.Key)) {
  240. cur.Value = value;
  241. return false;
  242. }
  243. prev = cur;
  244. cur = cur.Next;
  245. }
  246. if (prev != null) {
  247. Debug.Assert(prev.Next == null);
  248. prev.Next = new Bucket(hc, key, value, null);
  249. } else {
  250. buckets[index] = new Bucket(hc, key, value, null);
  251. }
  252. return true;
  253. }
  254. /// <summary>
  255. /// Removes an entry from the dictionary and returns true if the
  256. /// entry was removed or false.
  257. /// </summary>
  258. public override bool Remove(object key) {
  259. object dummy;
  260. return TryRemoveValue(key, out dummy);
  261. }
  262. /// <summary>
  263. /// Removes an entry from the dictionary and returns true if the
  264. /// entry was removed or false. The key will always be hashed
  265. /// so if it is unhashable an exception will be thrown - even
  266. /// if the dictionary has no buckets.
  267. /// </summary>
  268. internal bool RemoveAlwaysHash(object key) {
  269. lock (this) {
  270. object dummy;
  271. return TryRemoveNoLock(key, out dummy);
  272. }
  273. }
  274. public override bool TryRemoveValue(object key, out object value) {
  275. lock (this) {
  276. if (!HasAnyValues(_buckets)) {
  277. value = null;
  278. return false;
  279. }
  280. return TryRemoveNoLock(key, out value);
  281. }
  282. }
  283. private bool TryRemoveNoLock(object key, out object value) {
  284. Func<object, int> hashFunc;
  285. Func<object, object, bool> eqFunc;
  286. if (CompilerHelpers.GetType(key) == _keyType || _keyType == HeterogeneousType) {
  287. hashFunc = _hashFunc;
  288. eqFunc = _eqFunc;
  289. } else {
  290. hashFunc = _genericHash;
  291. eqFunc = _genericEquals;
  292. }
  293. int hc = hashFunc(key) & Int32.MaxValue;
  294. if (_buckets == null) {
  295. value = null;
  296. return false;
  297. }
  298. int index = hc % _buckets.Length;
  299. Bucket bucket = _buckets[index];
  300. Bucket prev = bucket;
  301. while (bucket != null) {
  302. if (bucket.HashCode == hc && eqFunc(key, bucket.Key)) {
  303. value = bucket.Value;
  304. if (prev == bucket) {
  305. _buckets[index] = bucket.Next;
  306. } else {
  307. prev.Next = bucket.Next;
  308. }
  309. _count--;
  310. _version++;
  311. return true;
  312. }
  313. prev = bucket;
  314. bucket = bucket.Next;
  315. }
  316. value = null;
  317. return false;
  318. }
  319. /// <summary>
  320. /// Checks to see if the key exists in the dictionary.
  321. /// </summary>
  322. public override bool Contains(object key) {
  323. return Contains(_buckets, key);
  324. }
  325. /// <summary>
  326. /// Static helper to see if the key exists in the provided bucket array.
  327. ///
  328. /// Used so the contains check can run against a buckets while a writer
  329. /// replaces the buckets.
  330. /// </summary>
  331. private bool Contains(Bucket[] buckets, object key) {
  332. object res;
  333. return TryGetValue(buckets, key, out res);
  334. }
  335. private bool HasAnyValues(Bucket[] buckets) {
  336. return buckets != null && _hashFunc != null;
  337. }
  338. /// <summary>
  339. /// Trys to get the value associated with the given key and returns true
  340. /// if it's found or false if it's not present.
  341. /// </summary>
  342. public override bool TryGetValue(object key, out object value) {
  343. return TryGetValue(_buckets, key, out value);
  344. }
  345. /// <summary>
  346. /// Static helper to try and get the value from the dictionary.
  347. ///
  348. /// Used so the value lookup can run against a buckets while a writer
  349. /// replaces the buckets.
  350. /// </summary>
  351. private bool TryGetValue(Bucket[] buckets, object key, out object value) {
  352. if (HasAnyValues(buckets)) {
  353. int hc;
  354. Func<object, object, bool> eqFunc;
  355. if (CompilerHelpers.GetType(key) == _keyType || _keyType == HeterogeneousType) {
  356. hc = _hashFunc(key) & Int32.MaxValue;
  357. eqFunc = _eqFunc;
  358. } else {
  359. hc = _genericHash(key) & Int32.MaxValue;
  360. eqFunc = _genericEquals;
  361. }
  362. Bucket bucket = buckets[hc % buckets.Length];
  363. while (bucket != null) {
  364. if (bucket.HashCode == hc && (Object.ReferenceEquals(key, bucket.Key) || eqFunc(key, bucket.Key))) {
  365. value = bucket.Value;
  366. return true;
  367. }
  368. bucket = bucket.Next;
  369. }
  370. }
  371. value = null;
  372. return false;
  373. }
  374. /// <summary>
  375. /// Returns the number of key/value pairs currently in the dictionary.
  376. /// </summary>
  377. public override int Count {
  378. get { return _count; }
  379. }
  380. /// <summary>
  381. /// Clears the contents of the dictionary.
  382. /// </summary>
  383. public override void Clear() {
  384. lock (this) {
  385. if (_buckets != null) {
  386. _buckets = new Bucket[8];
  387. _count = 0;
  388. _version++;
  389. }
  390. }
  391. }
  392. public override List<KeyValuePair<object, object>> GetItems() {
  393. lock (this) {
  394. List<KeyValuePair<object, object>> res = new List<KeyValuePair<object, object>>(Count);
  395. if (_buckets != null) {
  396. for (int i = 0; i < _buckets.Length; i++) {
  397. Bucket curBucket = _buckets[i];
  398. while (curBucket != null) {
  399. res.Add(new KeyValuePair<object, object>(curBucket.Key, curBucket.Value));
  400. curBucket = curBucket.Next;
  401. }
  402. }
  403. }
  404. return res;
  405. }
  406. }
  407. public override IEnumerable<object>/*!*/ GetKeys() {
  408. Bucket[] buckets = _buckets;
  409. lock (this) {
  410. object[] res = new object[Count];
  411. int index = 0;
  412. if (buckets != null) {
  413. for (int i = 0; i < buckets.Length; i++) {
  414. Bucket curBucket = buckets[i];
  415. while (curBucket != null) {
  416. res[index++] = curBucket.Key;
  417. curBucket = curBucket.Next;
  418. }
  419. }
  420. }
  421. return res;
  422. }
  423. }
  424. public override bool HasNonStringAttributes() {
  425. lock (this) {
  426. if (_keyType != typeof(string) && _buckets != null) {
  427. for (int i = 0; i < _buckets.Length; i++) {
  428. Bucket curBucket = _buckets[i];
  429. while (curBucket != null) {
  430. if (!(curBucket.Key is string)) {
  431. return true;
  432. }
  433. curBucket = curBucket.Next;
  434. }
  435. }
  436. }
  437. }
  438. return false;
  439. }
  440. /// <summary>
  441. /// Clones the storage returning a new DictionaryStorage object.
  442. /// </summary>
  443. public override DictionaryStorage Clone() {
  444. lock (this) {
  445. if (_buckets == null) {
  446. return new CommonDictionaryStorage();
  447. }
  448. Bucket[] resBuckets = new Bucket[_buckets.Length];
  449. for (int i = 0; i < _buckets.Length; i++) {
  450. if (_buckets[i] != null) {
  451. resBuckets[i] = _buckets[i].Clone();
  452. }
  453. }
  454. return new CommonDictionaryStorage(resBuckets, Count, _keyType, _hashFunc, _eqFunc);
  455. }
  456. }
  457. public override void CopyTo(DictionaryStorage/*!*/ into) {
  458. Debug.Assert(into != null);
  459. if (_buckets != null) {
  460. using (new OrderedLocker(this, into)) {
  461. CommonDictionaryStorage commonInto = into as CommonDictionaryStorage;
  462. if (commonInto != null) {
  463. CommonCopyTo(commonInto);
  464. } else {
  465. UncommonCopyTo(into);
  466. }
  467. }
  468. }
  469. }
  470. private void CommonCopyTo(CommonDictionaryStorage into) {
  471. if (into._buckets == null) {
  472. into._buckets = new Bucket[_buckets.Length];
  473. } else {
  474. int curSize = into._buckets.Length;
  475. while (curSize < _count + into._count) {
  476. curSize *= ResizeMultiplier;
  477. }
  478. into.EnsureSize(curSize);
  479. }
  480. if (into._keyType == null) {
  481. into._keyType = _keyType;
  482. into._hashFunc = _hashFunc;
  483. into._eqFunc = _eqFunc;
  484. } else if (into._keyType != _keyType) {
  485. SetHeterogeneousSites();
  486. }
  487. for (int i = 0; i < _buckets.Length; i++) {
  488. Bucket curBucket = _buckets[i];
  489. while (curBucket != null) {
  490. if (AddWorker(into._buckets, curBucket.Key, curBucket.Value, curBucket.HashCode)) {
  491. into._count++;
  492. }
  493. curBucket = curBucket.Next;
  494. }
  495. }
  496. }
  497. private void UncommonCopyTo(DictionaryStorage into) {
  498. for (int i = 0; i < _buckets.Length; i++) {
  499. Bucket curBucket = _buckets[i];
  500. while (curBucket != null) {
  501. into.AddNoLock(curBucket.Key, curBucket.Value);
  502. curBucket = curBucket.Next;
  503. }
  504. }
  505. }
  506. /// <summary>
  507. /// Helper to hash the given key w/ support for null.
  508. /// </summary>
  509. private int Hash(object key) {
  510. if (key is string) return key.GetHashCode() & Int32.MaxValue;
  511. return _hashFunc(key) & Int32.MaxValue;
  512. }
  513. /// <summary>
  514. /// Used to store a single hashed key/value and a linked list of
  515. /// collisions.
  516. ///
  517. /// Bucket is not serializable because it stores the computed hash
  518. /// code which could change between serialization and deserialization.
  519. /// </summary>
  520. private class Bucket {
  521. public object Key; // the key to be hashed
  522. public object Value; // the value associated with the key
  523. public Bucket Next; // the next chained bucket when there's a collision
  524. public int HashCode; // the hash code of the contained key.
  525. public Bucket() {
  526. }
  527. public Bucket(int hashCode, object key, object value, Bucket next) {
  528. HashCode = hashCode;
  529. Key = key;
  530. Value = value;
  531. Next = next;
  532. }
  533. public Bucket Clone() {
  534. return new Bucket(HashCode, Key, Value, CloneNext());
  535. }
  536. private Bucket CloneNext() {
  537. if (Next == null) return null;
  538. return Next.Clone();
  539. }
  540. }
  541. #region Hash/Equality Delegates
  542. private static int PrimitiveHash(object o) {
  543. return o.GetHashCode();
  544. }
  545. private static int IntHash(object o) {
  546. return (int)o;
  547. }
  548. private static int DoubleHash(object o) {
  549. return DoubleOps.__hash__((double)o);
  550. }
  551. private static int GenericHash(object o) {
  552. return PythonOps.Hash(DefaultContext.Default, o);
  553. }
  554. private static int TupleHash(object o) {
  555. return ((IStructuralEquatable)o).GetHashCode(
  556. DefaultContext.DefaultPythonContext.EqualityComparerNonGeneric
  557. );
  558. }
  559. private static bool StringEquals(object o1, object o2) {
  560. return (string)o1 == (string)o2;
  561. }
  562. private static bool IntEquals(object o1, object o2) {
  563. return (int)o1 == (int)o2;
  564. }
  565. private static bool DoubleEquals(object o1, object o2) {
  566. return (double)o1 == (double)o2;
  567. }
  568. private static bool TupleEquals(object o1, object o2) {
  569. return ((IStructuralEquatable)o1).Equals(
  570. o2, DefaultContext.DefaultPythonContext.EqualityComparerNonGeneric
  571. );
  572. }
  573. private static bool GenericEquals(object o1, object o2) {
  574. return PythonOps.EqualRetBool(o1, o2);
  575. }
  576. #endregion
  577. #if !SILVERLIGHT
  578. /// <summary>
  579. /// Special marker bucket used during deserialization to not add
  580. /// an extra field to the dictionary storage type.
  581. /// </summary>
  582. private class DeserializationBucket : Bucket {
  583. public readonly SerializationInfo/*!*/ SerializationInfo;
  584. public DeserializationBucket(SerializationInfo info) {
  585. SerializationInfo = info;
  586. }
  587. }
  588. private DeserializationBucket GetDeserializationBucket() {
  589. if (_buckets == null) {
  590. return null;
  591. }
  592. if (_buckets.Length != 1) {
  593. return null;
  594. }
  595. return _buckets[0] as DeserializationBucket;
  596. }
  597. #region ISerializable Members
  598. public void GetObjectData(SerializationInfo info, StreamingContext context) {
  599. info.AddValue("buckets", GetItems());
  600. }
  601. #endregion
  602. #region IDeserializationCallback Members
  603. void IDeserializationCallback.OnDeserialization(object sender) {
  604. DeserializationBucket bucket = GetDeserializationBucket();
  605. if (bucket == null) {
  606. // we've received multiple OnDeserialization callbacks, only
  607. // deserialize after the 1st one
  608. return;
  609. }
  610. SerializationInfo info = bucket.SerializationInfo;
  611. _buckets = null;
  612. var buckets = (List<KeyValuePair<object, object>>)info.GetValue("buckets", typeof(List<KeyValuePair<object, object>>));
  613. foreach (KeyValuePair<object, object> kvp in buckets) {
  614. Add(kvp.Key, kvp.Value);
  615. }
  616. }
  617. #endregion
  618. #endif
  619. }
  620. }