GlobHashTable.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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. #if PROFILE_DICTIONARY
  7. #include "DictionaryStats.h"
  8. #endif
  9. template <typename TData, typename TElement>
  10. class HashBucket
  11. {
  12. public:
  13. TElement element;
  14. TData value;
  15. public:
  16. HashBucket() : element(NULL), value(NULL) {}
  17. static void Copy(HashBucket const& bucket, HashBucket& newBucket)
  18. {
  19. newBucket.element = bucket.element;
  20. newBucket.value = bucket.value;
  21. }
  22. };
  23. class Key
  24. {
  25. public:
  26. static uint Get(Sym const *sym) { return static_cast<uint>(sym->m_id); }
  27. static uint Get(ExprHash hash) { return static_cast<uint>(hash); }
  28. };
  29. #define FOREACH_GLOBHASHTABLE_ENTRY(bucket, hashTable) \
  30. for (uint _iterHash = 0; _iterHash < (hashTable)->tableSize; _iterHash++) \
  31. { \
  32. FOREACH_SLISTBASE_ENTRY(GlobHashBucket, bucket, &(hashTable)->table[_iterHash]) \
  33. {
  34. #define NEXT_GLOBHASHTABLE_ENTRY \
  35. } \
  36. NEXT_SLISTBASE_ENTRY; \
  37. }
  38. template<typename TData, typename TElement>
  39. class ValueHashTable
  40. {
  41. private:
  42. typedef HashBucket<TData, TElement> HashBucket;
  43. public:
  44. JitArenaAllocator * alloc;
  45. uint tableSize;
  46. SListBase<HashBucket> * table;
  47. public:
  48. static ValueHashTable * New(JitArenaAllocator *allocator, DECLSPEC_GUARD_OVERFLOW uint tableSize)
  49. {
  50. return AllocatorNewPlus(JitArenaAllocator, allocator, (tableSize*sizeof(SListBase<HashBucket>)), ValueHashTable, allocator, tableSize);
  51. }
  52. void Delete()
  53. {
  54. AllocatorDeletePlus(JitArenaAllocator, alloc, (tableSize*sizeof(SListBase<HashBucket>)), this);
  55. }
  56. ~ValueHashTable()
  57. {
  58. for (uint i = 0; i< tableSize; i++)
  59. {
  60. table[i].Clear(alloc);
  61. }
  62. }
  63. SListBase<HashBucket> * SwapBucket(SListBase<HashBucket> * newTable)
  64. {
  65. SListBase<HashBucket> * retTable = table;
  66. table = newTable;
  67. return retTable;
  68. }
  69. TElement * FindOrInsertNew(TData value)
  70. {
  71. uint key = Key::Get(value);
  72. uint hash = this->Hash(key);
  73. #if PROFILE_DICTIONARY
  74. uint depth = 1;
  75. #endif
  76. // Keep sorted
  77. FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, &this->table[hash], iter)
  78. {
  79. if (Key::Get(bucket.value) <= key)
  80. {
  81. if (Key::Get(bucket.value) == key)
  82. {
  83. return &(bucket.element);
  84. }
  85. break;
  86. }
  87. #if PROFILE_DICTIONARY
  88. ++depth;
  89. #endif
  90. } NEXT_SLISTBASE_ENTRY_EDITING;
  91. HashBucket * newBucket = iter.InsertNodeBefore(this->alloc);
  92. newBucket->value = value;
  93. #if PROFILE_DICTIONARY
  94. if (stats)
  95. stats->Insert(depth);
  96. #endif
  97. return &newBucket->element;
  98. }
  99. TElement * FindOrInsertNewNoThrow(TData * value)
  100. {
  101. uint key = Key::Get(value);
  102. uint hash = this->Hash(key);
  103. #if PROFILE_DICTIONARY
  104. uint depth = 1;
  105. #endif
  106. // Keep sorted
  107. FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, &this->table[hash], iter)
  108. {
  109. if (Key::Get(bucket.value) <= key)
  110. {
  111. if (Key::Get(bucket.value) == key)
  112. {
  113. return &(bucket.element);
  114. }
  115. break;
  116. }
  117. #if PROFILE_DICTIONARY
  118. ++depth;
  119. #endif
  120. } NEXT_SLISTBASE_ENTRY_EDITING;
  121. HashBucket * newBucket = iter.InsertNodeBeforeNoThrow(this->alloc);
  122. if (newBucket == nullptr)
  123. {
  124. return nullptr;
  125. }
  126. newBucket->value = value;
  127. #if PROFILE_DICTIONARY
  128. if (stats)
  129. stats->Insert(depth);
  130. #endif
  131. return &newBucket->element;
  132. }
  133. TElement * FindOrInsert(TElement element, TData value)
  134. {
  135. uint key = Key::Get(value);
  136. uint hash = this->Hash(key);
  137. #if PROFILE_DICTIONARY
  138. uint depth = 1;
  139. #endif
  140. // Keep sorted
  141. FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, &this->table[hash], iter)
  142. {
  143. if (Key::Get(bucket.value) <= key)
  144. {
  145. if (Key::Get(bucket.value) == key)
  146. {
  147. return &(bucket.element);
  148. }
  149. break;
  150. }
  151. #if PROFILE_DICTIONARY
  152. ++depth;
  153. #endif
  154. } NEXT_SLISTBASE_ENTRY_EDITING;
  155. HashBucket * newBucket = iter.InsertNodeBefore(this->alloc);
  156. Assert(newBucket != nullptr);
  157. newBucket->value = value;
  158. newBucket->element = element;
  159. #if PROFILE_DICTIONARY
  160. if (stats)
  161. stats->Insert(depth);
  162. #endif
  163. return NULL;
  164. }
  165. TElement * Get(TData value)
  166. {
  167. uint key = Key::Get(value);
  168. return Get(key);
  169. }
  170. TElement * Get(uint key)
  171. {
  172. uint hash = this->Hash(key);
  173. // Assumes sorted lists
  174. FOREACH_SLISTBASE_ENTRY(HashBucket, bucket, &this->table[hash])
  175. {
  176. if (Key::Get(bucket.value) <= key)
  177. {
  178. if (Key::Get(bucket.value) == key)
  179. {
  180. return &(bucket.element);
  181. }
  182. break;
  183. }
  184. } NEXT_SLISTBASE_ENTRY;
  185. return NULL;
  186. }
  187. HashBucket * GetBucket(uint key)
  188. {
  189. uint hash = this->Hash(key);
  190. // Assumes sorted lists
  191. FOREACH_SLISTBASE_ENTRY(HashBucket, bucket, &this->table[hash])
  192. {
  193. if (Key::Get(bucket.value) <= key)
  194. {
  195. if (Key::Get(bucket.value) == key)
  196. {
  197. return &bucket;
  198. }
  199. break;
  200. }
  201. } NEXT_SLISTBASE_ENTRY;
  202. return nullptr;
  203. }
  204. TElement GetAndClear(TData * value)
  205. {
  206. uint key = Key::Get(value);
  207. uint hash = this->Hash(key);
  208. SListBase<HashBucket> * list = &this->table[hash];
  209. #if PROFILE_DICTIONARY
  210. bool first = true;
  211. #endif
  212. // Assumes sorted lists
  213. FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, list, iter)
  214. {
  215. if (Key::Get(bucket.value) <= key)
  216. {
  217. if (Key::Get(bucket.value) == key)
  218. {
  219. TElement retVal = bucket.element;
  220. iter.RemoveCurrent(this->alloc);
  221. #if PROFILE_DICTIONARY
  222. if (stats)
  223. stats->Remove(first && !(iter.Next()));
  224. #endif
  225. return retVal;
  226. }
  227. break;
  228. }
  229. #if PROFILE_DICTIONARY
  230. first = false;
  231. #endif
  232. } NEXT_SLISTBASE_ENTRY_EDITING;
  233. return nullptr;
  234. }
  235. void Clear(uint key)
  236. {
  237. uint hash = this->Hash(key);
  238. SListBase<HashBucket> * list = &this->table[hash];
  239. // Assumes sorted lists
  240. #if PROFILE_DICTIONARY
  241. bool first = true;
  242. #endif
  243. FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, list, iter)
  244. {
  245. if (Key::Get(bucket.value) <= key)
  246. {
  247. if (Key::Get(bucket.value) == key)
  248. {
  249. iter.RemoveCurrent(this->alloc);
  250. #if PROFILE_DICTIONARY
  251. if (stats)
  252. stats->Remove(first && !(iter.Next()));
  253. #endif
  254. }
  255. return;
  256. }
  257. #if PROFILE_DICTIONARY
  258. first = false;
  259. #endif
  260. } NEXT_SLISTBASE_ENTRY_EDITING;
  261. }
  262. void And(ValueHashTable *this2)
  263. {
  264. for (uint i = 0; i < this->tableSize; i++)
  265. {
  266. _TYPENAME SListBase<HashBucket>::Iterator iter2(&this2->table[i]);
  267. iter2.Next();
  268. FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, &this->table[i], iter)
  269. {
  270. while (iter2.IsValid() && bucket.value < iter2.Data().value)
  271. {
  272. iter2.Next();
  273. }
  274. if (!iter2.IsValid() || bucket.value != iter2.Data().value || bucket.element != iter2.Data().element)
  275. {
  276. iter.RemoveCurrent(this->alloc);
  277. #if PROFILE_DICTIONARY
  278. if (stats)
  279. stats->Remove(false);
  280. #endif
  281. continue;
  282. }
  283. else
  284. {
  285. AssertMsg(bucket.value == iter2.Data().value && bucket.element == iter2.Data().element, "Huh??");
  286. }
  287. iter2.Next();
  288. } NEXT_SLISTBASE_ENTRY_EDITING;
  289. }
  290. }
  291. template <class Fn>
  292. void Or(ValueHashTable * this2, Fn fn)
  293. {
  294. for (uint i = 0; i < this->tableSize; i++)
  295. {
  296. _TYPENAME SListBase<HashBucket>::Iterator iter2(&this2->table[i]);
  297. iter2.Next();
  298. FOREACH_SLISTBASE_ENTRY_EDITING((HashBucket), bucket, &this->table[i], iter)
  299. {
  300. while (iter2.IsValid() && bucket.value < iter2.Data().value)
  301. {
  302. HashBucket * newBucket = iter.InsertNodeBefore(this->alloc);
  303. newBucket->value = iter2.Data().value;
  304. newBucket->element = fn(null, iter2.Data().element);
  305. iter2.Next();
  306. }
  307. if (!iter2.IsValid())
  308. {
  309. break;
  310. }
  311. if (bucket.value == iter2.Data().value)
  312. {
  313. bucket.element = fn(bucket.element, iter2.Data().element);
  314. iter2.Next();
  315. }
  316. } NEXT_SLISTBASE_ENTRY_EDITING;
  317. while (iter2.IsValid())
  318. {
  319. HashBucket * newBucket = iter.InsertNodeBefore(this->alloc);
  320. newBucket->value = iter2.Data().value;
  321. newBucket->element = fn(null, iter2.Data().element);
  322. iter2.Next();
  323. }
  324. }
  325. }
  326. ValueHashTable *Copy()
  327. {
  328. ValueHashTable *newTable = ValueHashTable::New(this->alloc, this->tableSize);
  329. for (uint i = 0; i < this->tableSize; i++)
  330. {
  331. this->table[i].template CopyTo<HashBucket::Copy>(this->alloc, newTable->table[i]);
  332. }
  333. #if PROFILE_DICTIONARY
  334. if (stats)
  335. newTable->stats = stats->Clone();
  336. #endif
  337. return newTable;
  338. }
  339. void ClearAll()
  340. {
  341. for (uint i = 0; i < this->tableSize; i++)
  342. {
  343. this->table[i].Clear(this->alloc);
  344. }
  345. #if PROFILE_DICTIONARY
  346. // To not lose previously collected data, we will treat cleared dictionary as a separate instance for stats tracking purpose
  347. stats = DictionaryStats::Create(typeid(this).name(), tableSize);
  348. #endif
  349. }
  350. #if DBG_DUMP
  351. void Dump()
  352. {
  353. FOREACH_GLOBHASHTABLE_ENTRY(bucket, this)
  354. {
  355. Output::Print(_u("%4d => "), bucket.value);
  356. bucket.element->Dump();
  357. Output::Print(_u("\n"));
  358. Output::Print(_u("\n"));
  359. }
  360. NEXT_GLOBHASHTABLE_ENTRY;
  361. }
  362. void Dump(void (*valueDump)(TData))
  363. {
  364. Output::Print(_u("\n-------------------------------------------------------------------------------------------------\n"));
  365. FOREACH_GLOBHASHTABLE_ENTRY(bucket, this)
  366. {
  367. valueDump(bucket.value);
  368. Output::Print(_u(" => "), bucket.value);
  369. bucket.element->Dump();
  370. Output::Print(_u("\n"));
  371. }
  372. NEXT_GLOBHASHTABLE_ENTRY;
  373. }
  374. #endif
  375. protected:
  376. ValueHashTable(JitArenaAllocator * allocator, uint tableSize) : alloc(allocator), tableSize(tableSize)
  377. {
  378. Init();
  379. #if PROFILE_DICTIONARY
  380. stats = DictionaryStats::Create(typeid(this).name(), tableSize);
  381. #endif
  382. }
  383. void Init()
  384. {
  385. table = (SListBase<HashBucket> *)(((char *)this) + sizeof(ValueHashTable));
  386. for (uint i = 0; i < tableSize; i++)
  387. {
  388. // placement new
  389. ::new (&table[i]) SListBase<HashBucket>();
  390. }
  391. }
  392. private:
  393. uint Hash(uint key) { return (key % this->tableSize); }
  394. #if PROFILE_DICTIONARY
  395. DictionaryStats *stats;
  396. #endif
  397. };