Dictionary.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. namespace JsUtil
  7. {
  8. template <class TKey, class TValue> class WeakRefDictionaryEntry
  9. {
  10. public:
  11. static const int INVALID_HASH_VALUE = 0;
  12. hash_t hash; // Lower 31 bits of hash code << 1 | 1, 0 if unused
  13. int next; // Index of next entry, -1 if last
  14. Field(const RecyclerWeakReference<TKey>*) key; // Key of entry- this entry holds a weak reference to the key
  15. TValue value; // Value of entry
  16. };
  17. // TODO: convert to BaseDictionary- easier now to have custom dictionary since this does compacting
  18. // and weak reference resolution
  19. template <class TKey, class TValue, class KeyComparer = DefaultComparer<const TKey*>, bool cleanOnInsert = true> class WeaklyReferencedKeyDictionary
  20. {
  21. public:
  22. typedef WeakRefDictionaryEntry<TKey, Field(TValue)> EntryType;
  23. typedef TKey KeyType;
  24. typedef TValue ValueType;
  25. typedef void (*EntryRemovalCallbackMethodType)(const EntryType& e, void* cookie);
  26. struct EntryRemovalCallback
  27. {
  28. FieldNoBarrier(EntryRemovalCallbackMethodType) fnCallback;
  29. Field(void*) cookie;
  30. };
  31. private:
  32. Field(int) size;
  33. Field(int*) buckets;
  34. Field(EntryType *) entries;
  35. Field(int) count;
  36. Field(int) version;
  37. Field(int) freeList;
  38. Field(int) freeCount;
  39. FieldNoBarrier(Recycler*) recycler;
  40. FieldNoBarrier(EntryRemovalCallback) entryRemovalCallback;
  41. Field(uint) lastWeakReferenceCleanupId;
  42. Field(bool) disableCleanup;
  43. public:
  44. // Allow WeaklyReferencedKeyDictionary field to be inlined in classes with DEFINE_VTABLE_CTOR_MEMBER_INIT
  45. WeaklyReferencedKeyDictionary(VirtualTableInfoCtorEnum) { }
  46. WeaklyReferencedKeyDictionary(Recycler* recycler, int capacity = 0, EntryRemovalCallback* pEntryRemovalCallback = nullptr):
  47. buckets(nullptr),
  48. size(0),
  49. entries(nullptr),
  50. count(0),
  51. version(0),
  52. freeList(0),
  53. freeCount(0),
  54. recycler(recycler),
  55. lastWeakReferenceCleanupId(recycler->GetWeakReferenceCleanupId()),
  56. disableCleanup(false)
  57. {
  58. if (pEntryRemovalCallback != nullptr)
  59. {
  60. this->entryRemovalCallback.fnCallback = pEntryRemovalCallback->fnCallback;
  61. this->entryRemovalCallback.cookie = pEntryRemovalCallback->cookie;
  62. }
  63. else
  64. {
  65. this->entryRemovalCallback.fnCallback = nullptr;
  66. }
  67. if (capacity > 0) { Initialize(capacity); }
  68. }
  69. ~WeaklyReferencedKeyDictionary()
  70. {
  71. }
  72. inline int Count()
  73. {
  74. return count - freeCount;
  75. }
  76. TValue Item(TKey* key)
  77. {
  78. int i = FindEntry(key);
  79. if (i >= 0) return entries[i].value;
  80. Js::Throw::FatalInternalError();
  81. }
  82. void Item(TKey* key, const TValue value)
  83. {
  84. Insert(key, value, false);
  85. }
  86. const TValue& GetValueAt(const int& index) const
  87. {
  88. if (index >= 0 && index < count)
  89. {
  90. return entries[index].value;
  91. }
  92. Js::Throw::FatalInternalError();
  93. }
  94. bool TryGetValue(const TKey* key, TValue* value)
  95. {
  96. int i = FindEntry<TKey>(key);
  97. if (i >= 0)
  98. {
  99. *value = entries[i].value;
  100. return true;
  101. }
  102. return false;
  103. }
  104. bool TryGetValueAndRemove(const TKey* key, TValue* value)
  105. {
  106. if (buckets == nullptr) return false;
  107. hash_t hash = GetHashCode(key);
  108. uint targetBucket = hash % size;
  109. int last = -1;
  110. int i = 0;
  111. if ((i = FindEntry<TKey>(key, hash, targetBucket, last)) != -1)
  112. {
  113. *value = entries[i].value;
  114. RemoveEntry(i, last, targetBucket);
  115. return true;
  116. }
  117. return false;
  118. }
  119. template <typename TLookup>
  120. inline TValue Lookup(const TLookup* key, TValue defaultValue, __out TKey const** pKeyOut)
  121. {
  122. int i = FindEntry(key);
  123. if (i >= 0)
  124. {
  125. (*pKeyOut) = entries[i].key->Get();
  126. return entries[i].value;
  127. }
  128. (*pKeyOut) = nullptr;
  129. return defaultValue;
  130. }
  131. inline TValue Lookup(const TKey* key, TValue defaultValue)
  132. {
  133. int i = FindEntry(key);
  134. if (i >= 0)
  135. {
  136. return entries[i].value;
  137. }
  138. return defaultValue;
  139. }
  140. const RecyclerWeakReference<TKey>* Add(TKey* key, TValue value)
  141. {
  142. return Insert(key, value, true);
  143. }
  144. const RecyclerWeakReference<TKey>* UncheckedAdd(TKey* key, TValue value)
  145. {
  146. return Insert(key, value, true, false);
  147. }
  148. const RecyclerWeakReference<TKey>* UncheckedAdd(const RecyclerWeakReference<TKey>* weakRef, TValue value)
  149. {
  150. return UncheckedInsert(weakRef, value);
  151. }
  152. template<class Fn>
  153. void Map(Fn fn)
  154. {
  155. for(int i = 0; i < size; i++)
  156. {
  157. if(buckets[i] != -1)
  158. {
  159. for(int previousIndex = -1, currentIndex = buckets[i]; currentIndex != -1;)
  160. {
  161. EntryType &currentEntry = entries[currentIndex];
  162. TKey * key = currentEntry.key->Get();
  163. if(key != nullptr)
  164. {
  165. fn(key, currentEntry.value, currentEntry.key);
  166. // Keep the entry
  167. previousIndex = currentIndex;
  168. currentIndex = currentEntry.next;
  169. }
  170. else
  171. {
  172. // Remove the entry
  173. const int nextIndex = currentEntry.next;
  174. RemoveEntry(currentIndex, previousIndex, i);
  175. currentIndex = nextIndex;
  176. }
  177. }
  178. }
  179. }
  180. }
  181. void SetDisableCleanup(bool disableCleanup)
  182. {
  183. this->disableCleanup = disableCleanup;
  184. }
  185. bool GetDisableCleanup()
  186. {
  187. return this->disableCleanup;
  188. }
  189. bool Clean()
  190. {
  191. if (!disableCleanup && recycler->GetWeakReferenceCleanupId() != this->lastWeakReferenceCleanupId)
  192. {
  193. Map([](TKey * key, TValue value, const RecyclerWeakReference<TKey>* weakRef) {});
  194. this->lastWeakReferenceCleanupId = recycler->GetWeakReferenceCleanupId();
  195. }
  196. return freeCount > 0;
  197. }
  198. void Clear()
  199. {
  200. if (count > 0)
  201. {
  202. for (int i = 0; i < size; i++) buckets[i] = -1;
  203. ClearArray(entries, size);
  204. freeList = -1;
  205. count = 0;
  206. freeCount = 0;
  207. }
  208. }
  209. void EnsureCapacity()
  210. {
  211. if (freeCount == 0 && count == size)
  212. {
  213. if (cleanOnInsert && Clean())
  214. {
  215. Assert(freeCount > 0);
  216. }
  217. else
  218. {
  219. Resize();
  220. }
  221. }
  222. }
  223. private:
  224. const RecyclerWeakReference<TKey>* UncheckedInsert(const RecyclerWeakReference<TKey>* weakRef, TValue value)
  225. {
  226. if (buckets == nullptr) Initialize(0);
  227. int hash = GetHashCode(weakRef->FastGet());
  228. uint bucket = (uint)hash % size;
  229. Assert(FindEntry(weakRef->FastGet()) == -1);
  230. return Insert(weakRef, value, hash, bucket);
  231. }
  232. const RecyclerWeakReference<TKey>* Insert(TKey* key, TValue value, bool add, bool checkForExisting = true)
  233. {
  234. if (buckets == nullptr) Initialize(0);
  235. hash_t hash = GetHashCode(key);
  236. uint bucket = hash % size;
  237. if (checkForExisting)
  238. {
  239. int previous = -1;
  240. int i = FindEntry(key, hash, bucket, previous);
  241. if (i != -1)
  242. {
  243. if (add)
  244. {
  245. Js::Throw::FatalInternalError();
  246. }
  247. entries[i].value = value;
  248. version++;
  249. return entries[i].key;
  250. }
  251. }
  252. // We know we need to insert- so first try creating the weak reference, before adding it to
  253. // the dictionary. If we OOM here, we still leave the dictionary as we found it.
  254. const RecyclerWeakReference<TKey>* weakRef = recycler->CreateWeakReferenceHandle<TKey>(key);
  255. return Insert(weakRef, value, hash, bucket);
  256. }
  257. const RecyclerWeakReference<TKey>* Insert(const RecyclerWeakReference<TKey>* weakRef, TValue value, int hash, uint bucket)
  258. {
  259. int index;
  260. if (freeCount > 0)
  261. {
  262. index = freeList;
  263. freeList = entries[index].next;
  264. freeCount--;
  265. }
  266. else
  267. {
  268. if (count == size)
  269. {
  270. if (cleanOnInsert && Clean())
  271. {
  272. index = freeList;
  273. freeList = entries[index].next;
  274. freeCount--;
  275. }
  276. else
  277. {
  278. Resize();
  279. bucket = (uint)hash % size;
  280. index = count;
  281. count++;
  282. }
  283. }
  284. else
  285. {
  286. index = count;
  287. count++;
  288. }
  289. }
  290. entries[index].next = buckets[bucket];
  291. entries[index].key = weakRef;
  292. entries[index].hash = hash;
  293. entries[index].value = value;
  294. buckets[bucket] = index;
  295. version++;
  296. return entries[index].key;
  297. }
  298. void Resize()
  299. {
  300. int newSize = PrimePolicy::GetSize(count * 2);
  301. if (newSize <= count)
  302. {
  303. // throw OOM if we can't increase the dictionary size
  304. Js::Throw::OutOfMemory();
  305. }
  306. int* newBuckets = RecyclerNewArrayLeaf(recycler, int, newSize);
  307. for (int i = 0; i < newSize; i++) newBuckets[i] = -1;
  308. EntryType* newEntries = RecyclerNewArray(recycler, EntryType, newSize);
  309. CopyArray<EntryType, Field(const RecyclerWeakReference<TKey>*)>(newEntries, newSize, entries, count);
  310. AnalysisAssert(count < newSize);
  311. for (int i = 0; i < count; i++)
  312. {
  313. uint bucket = (uint)newEntries[i].hash % newSize;
  314. newEntries[i].next = newBuckets[bucket];
  315. newBuckets[bucket] = i;
  316. }
  317. buckets = newBuckets;
  318. size = newSize;
  319. entries = newEntries;
  320. }
  321. template <typename TLookup>
  322. inline hash_t GetHashCode(const TLookup* key)
  323. {
  324. return TAGHASH(KeyComparer::GetHashCode(key));
  325. }
  326. template <typename TLookup>
  327. inline int FindEntry(const TLookup* key)
  328. {
  329. if (buckets != nullptr)
  330. {
  331. hash_t hash = GetHashCode(key);
  332. uint bucket = (uint)hash % size;
  333. int previous = -1;
  334. return FindEntry(key, hash, bucket, previous);
  335. }
  336. return -1;
  337. }
  338. template <typename TLookup>
  339. inline int FindEntry(const TLookup* key, hash_t const hash, uint& bucket, int& previous)
  340. {
  341. if (buckets != nullptr)
  342. {
  343. BOOL inSweep = this->recycler->IsSweeping();
  344. previous = -1;
  345. for (int i = buckets[bucket]; i >= 0; )
  346. {
  347. if (entries[i].hash == hash)
  348. {
  349. TKey* strongRef = nullptr;
  350. if (!inSweep)
  351. {
  352. // Quickly check for null if we're not in sweep- if it's null, it's definitely been collected
  353. // so remove
  354. strongRef = entries[i].key->FastGet();
  355. }
  356. else
  357. {
  358. // If we're in sweep, use the slower Get which checks if the object is getting collected
  359. // This could return null too but we won't clean it up now, we'll clean it up later
  360. strongRef = entries[i].key->Get();
  361. }
  362. if (strongRef == nullptr)
  363. {
  364. i = RemoveEntry(i, previous, bucket);
  365. continue;
  366. }
  367. else
  368. {
  369. // if we get here, strongRef is not null
  370. if (KeyComparer::Equals(strongRef, key))
  371. return i;
  372. }
  373. }
  374. previous = i;
  375. i = entries[i].next;
  376. }
  377. }
  378. return -1;
  379. }
  380. void Initialize(int capacity)
  381. {
  382. int size = PrimePolicy::GetSize(capacity);
  383. int* buckets = RecyclerNewArrayLeaf(recycler, int, size);
  384. EntryType * entries = RecyclerNewArray(recycler, EntryType, size);
  385. // No need for auto pointers here since these are both recycler
  386. // allocated objects
  387. if (buckets != nullptr && entries != nullptr)
  388. {
  389. this->size = size;
  390. this->buckets = buckets;
  391. for (int i = 0; i < size; i++) buckets[i] = -1;
  392. this->entries = entries;
  393. this->freeList = -1;
  394. }
  395. }
  396. int RemoveEntry(int i, int previous, uint bucket)
  397. {
  398. int next = entries[i].next;
  399. if (previous < 0) // Previous < 0 => first node
  400. {
  401. buckets[bucket] = entries[i].next;
  402. }
  403. else
  404. {
  405. entries[previous].next = entries[i].next;
  406. }
  407. if (this->entryRemovalCallback.fnCallback != nullptr)
  408. {
  409. this->entryRemovalCallback.fnCallback(entries[i], this->entryRemovalCallback.cookie);
  410. }
  411. entries[i].next = freeList;
  412. entries[i].key = nullptr;
  413. entries[i].hash = EntryType::INVALID_HASH_VALUE;
  414. // Hold onto the pid here so that we can reuse it
  415. // entries[i].value = nullptr;
  416. freeList = i;
  417. freeCount++;
  418. version++;
  419. return next;
  420. }
  421. };
  422. }