MultiDictionary.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. #pragma once
  2. #include "../Common.h"
  3. #include "Array.h"
  4. NS_BF_BEGIN;
  5. struct MultiDictionaryFuncs : AllocatorCLib
  6. {
  7. template <typename T>
  8. size_t GetHash(const T& value)
  9. {
  10. return BeefHash<T>()(value);
  11. }
  12. template <typename T>
  13. bool Matches(const T& lhs, const T& rhs)
  14. {
  15. return lhs == rhs;
  16. }
  17. };
  18. template <typename TKey, typename TValue, typename TFuncs = MultiDictionaryFuncs>
  19. class MultiDictionary : public TFuncs
  20. {
  21. public:
  22. struct Entry
  23. {
  24. TKey mKey;
  25. TValue mValue;
  26. int mNext;
  27. int mHashCode;
  28. };
  29. struct EntryRef
  30. {
  31. public:
  32. MultiDictionary* mSet;
  33. int mIndex;
  34. public:
  35. EntryRef()
  36. {
  37. mSet = NULL;
  38. mIndex = -1;
  39. }
  40. EntryRef(MultiDictionary* set, int index)
  41. {
  42. mSet = set;
  43. mIndex = index;
  44. }
  45. Entry* operator*()
  46. {
  47. return &this->mSet->mEntries[this->mIndex];
  48. }
  49. Entry* operator->()
  50. {
  51. return &this->mSet->mEntries[this->mIndex];
  52. }
  53. operator bool() const
  54. {
  55. return this->mIndex != -1;
  56. }
  57. };
  58. struct Iterator
  59. {
  60. public:
  61. MultiDictionary* mDict;
  62. int mCurEntry;
  63. int mCurBucket;
  64. protected:
  65. Iterator()
  66. {
  67. this->mDict = NULL;
  68. this->mCurBucket = 0;
  69. this->mCurEntry = -1;
  70. }
  71. public:
  72. Iterator(MultiDictionary* dict)
  73. {
  74. this->mDict = dict;
  75. this->mCurBucket = 0;
  76. this->mCurEntry = -1;
  77. }
  78. Iterator& operator++()
  79. {
  80. if (this->mCurEntry != -1)
  81. {
  82. this->mCurEntry = this->mDict->mEntries[this->mCurEntry].mNext;
  83. if (this->mCurEntry != -1)
  84. return *this;
  85. this->mCurBucket++;
  86. }
  87. if (mDict->mHashHeads == NULL)
  88. {
  89. this->mCurBucket = this->mDict->mHashSize;
  90. return *this; // At end
  91. }
  92. while (this->mCurBucket < mDict->mHashSize)
  93. {
  94. this->mCurEntry = this->mDict->mHashHeads[mCurBucket];
  95. if (this->mCurEntry != -1)
  96. return *this;
  97. this->mCurBucket++;
  98. }
  99. return *this; // At end
  100. }
  101. TKey& GetKey()
  102. {
  103. return this->mDict->mEntries[this->mCurEntry].mKey;
  104. }
  105. TValue& GetValue()
  106. {
  107. return this->mDict->mEntries[this->mCurEntry].mValue;
  108. }
  109. bool operator!=(const Iterator& itr) const
  110. {
  111. return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket));
  112. }
  113. operator bool() const
  114. {
  115. return this->mCurEntry != -1;
  116. }
  117. void MoveToNextHashMatch()
  118. {
  119. int wantHash = this->mDict->mEntries[this->mCurEntry].mHashCode;
  120. do
  121. {
  122. this->mCurEntry = this->mDict->mEntries[this->mCurEntry].mNext;
  123. } while ((this->mCurEntry != -1) && (this->mDict->mEntries[this->mCurEntry].mHashCode != wantHash));
  124. if (this->mCurEntry == -1)
  125. this->mCurBucket = mDict->mHashSize;
  126. }
  127. };
  128. struct MatchIterator : public Iterator
  129. {
  130. public:
  131. TKey mKey;
  132. public:
  133. MatchIterator(const Iterator& iterator)
  134. {
  135. this->mDict = iterator.mDict;
  136. this->mCurBucket = iterator.mCurBucket;
  137. this->mCurEntry = iterator.mCurEntry;
  138. }
  139. MatchIterator(MultiDictionary* dict, const TKey& key) : Iterator(dict)
  140. {
  141. mKey = key;
  142. }
  143. MatchIterator& operator++()
  144. {
  145. while (true)
  146. {
  147. MoveToNextHashMatch();
  148. if (*this == mDict->end())
  149. break;
  150. if (mKey == GetKey())
  151. break;
  152. }
  153. return *this;
  154. }
  155. };
  156. protected:
  157. int GetPrimeish(int min)
  158. {
  159. // This is a minimal effort to help address-aligned dataa
  160. return (min | 1);
  161. }
  162. int ExpandSize(int oldSize)
  163. {
  164. int newSize = 2 * oldSize;
  165. // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
  166. // Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
  167. /*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
  168. {
  169. Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
  170. return MaxPrimeArrayLength;
  171. }*/
  172. return GetPrimeish(newSize);
  173. }
  174. void ResizeEntries()
  175. {
  176. ResizeEntries(ExpandSize(mCount));
  177. }
  178. void ResizeEntries(int newSize)
  179. {
  180. BF_ASSERT(newSize >= mAllocSize);
  181. Entry* newEntries = TFuncs::allocate<Entry>(newSize);
  182. for (int i = 0; i < mCount; i++)
  183. {
  184. auto& newEntry = newEntries[i];
  185. auto& oldEntry = mEntries[i];
  186. newEntry.mHashCode = oldEntry.mHashCode;
  187. newEntry.mNext = oldEntry.mNext;
  188. new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
  189. new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
  190. }
  191. for (int i = mCount; i < newSize; i++)
  192. {
  193. newEntries[i].mHashCode = -1;
  194. }
  195. TFuncs::deallocate(mEntries);
  196. mEntries = newEntries;
  197. mAllocSize = (int)newSize;
  198. }
  199. void FreeIdx(int entryIdx)
  200. {
  201. this->mEntries[entryIdx].mNext = this->mFreeList;
  202. this->mFreeList = entryIdx;
  203. this->mFreeCount++;
  204. }
  205. int AllocEntry()
  206. {
  207. int index;
  208. if (this->mFreeCount > 0)
  209. {
  210. index = this->mFreeList;
  211. this->mFreeList = this->mEntries[index].mNext;
  212. this->mFreeCount--;
  213. }
  214. else
  215. {
  216. if (this->mCount == this->mAllocSize)
  217. ResizeEntries();
  218. index = mCount;
  219. this->mCount++;
  220. }
  221. return index;
  222. }
  223. public:
  224. int* mHashHeads;
  225. int mAllocSize;
  226. Entry* mEntries;
  227. int mFreeList;
  228. int mFreeCount;
  229. static const int cDefaultHashSize = 17;
  230. int mHashSize;
  231. int mCount;
  232. MultiDictionary()
  233. {
  234. this->mHashHeads = NULL;
  235. this->mHashSize = cDefaultHashSize;
  236. this->mEntries = NULL;
  237. this->mAllocSize = 0;
  238. this->mCount = 0;
  239. this->mFreeList = -1;
  240. this->mFreeCount = 0;
  241. }
  242. ~MultiDictionary()
  243. {
  244. this->Clear();
  245. }
  246. void EnsureFreeCount(int wantFreeCount)
  247. {
  248. int freeCount = mFreeCount + (mAllocSize - mCount);
  249. if (freeCount >= wantFreeCount)
  250. return;
  251. ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount));
  252. }
  253. int GetCount() const
  254. {
  255. return mCount - mFreeCount;
  256. }
  257. int size() const
  258. {
  259. return mCount - mFreeCount;
  260. }
  261. EntryRef AddRaw(int hash)
  262. {
  263. if (this->mHashHeads == NULL)
  264. {
  265. this->mHashHeads = TFuncs::allocate<int>(mHashSize);
  266. memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
  267. }
  268. int index = AllocEntry();
  269. int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
  270. int headEntry = this->mHashHeads[hashIdx];
  271. Entry* newEntry = &mEntries[index];
  272. newEntry->mValue = T();
  273. newEntry->mNext = headEntry;
  274. newEntry->mHashCode = hash;
  275. mHashHeads[hashIdx] = index;
  276. return EntryRef(this, index);
  277. }
  278. void Add(TKey key, TValue value)
  279. {
  280. if (this->mHashHeads == NULL)
  281. {
  282. this->mHashHeads = TFuncs::allocate<int>(mHashSize);
  283. memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
  284. }
  285. int index = AllocEntry();
  286. int hash = TFuncs::GetHash(key);
  287. int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
  288. int headEntry = this->mHashHeads[hashIdx];
  289. Entry* newEntry = &mEntries[index];
  290. newEntry->mKey = key;
  291. newEntry->mValue = value;
  292. newEntry->mNext = headEntry;
  293. newEntry->mHashCode = hash;
  294. mHashHeads[hashIdx] = index;
  295. }
  296. void AddAfter(TKey key, TValue value, Entry* afterEntry)
  297. {
  298. int hash = TFuncs::GetHash(key);
  299. int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
  300. BF_ASSERT(hash == afterEntry->mHashCode);
  301. int index = AllocEntry();
  302. Entry* newEntry = &mEntries[index];
  303. newEntry->mKey = key;
  304. newEntry->mValue = value;
  305. newEntry->mNext = afterEntry->mNext;
  306. newEntry->mHashCode = hash;
  307. afterEntry->mNext = index;
  308. }
  309. void Rehash(int newHashSize)
  310. {
  311. auto newHashHeads = TFuncs::allocate<int>(newHashSize);
  312. memset(newHashHeads, -1, sizeof(int) * newHashSize);
  313. if (mHashHeads != NULL)
  314. {
  315. SizedArray<int, 1024> entryList;
  316. for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
  317. {
  318. int checkEntryIdx = mHashHeads[hashIdx];
  319. if (checkEntryIdx != -1)
  320. {
  321. // We want to keep elements with equal hashes in their insert order so we need to
  322. // iterate through the linked list in reverse
  323. entryList.Clear();
  324. while (checkEntryIdx != -1)
  325. {
  326. entryList.Add(checkEntryIdx);
  327. checkEntryIdx = mEntries[checkEntryIdx].mNext;
  328. }
  329. for (int i = (int)entryList.mSize - 1; i >= 0; i--)
  330. {
  331. int checkEntryIdx = entryList[i];
  332. auto checkEntry = &mEntries[checkEntryIdx];
  333. int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize;
  334. checkEntry->mNext = newHashHeads[newHashIdx];
  335. newHashHeads[newHashIdx] = checkEntryIdx;
  336. }
  337. }
  338. }
  339. TFuncs::deallocate(mHashHeads);
  340. }
  341. mHashHeads = newHashHeads;
  342. mHashSize = newHashSize;
  343. }
  344. void CheckRehash()
  345. {
  346. // Make the lookup load reasonable
  347. if (mHashSize < mCount)
  348. {
  349. this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1);
  350. }
  351. }
  352. template <typename TKey>
  353. bool TryGet(const TKey& key, TKey* outKey, TValue* outValue)
  354. {
  355. if (mHashHeads == NULL)
  356. return false;
  357. this->CheckRehash();
  358. int hash = TFuncs::GetHash(key);
  359. int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
  360. int checkEntryIdx = this->mHashHeads[hashIdx];
  361. while (checkEntryIdx != -1)
  362. {
  363. Entry* checkEntry = &mEntries[checkEntryIdx];
  364. if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
  365. {
  366. if (outKey != NULL)
  367. *outKey = checkEntry->mKey;
  368. if (outValue != NULL)
  369. *outValue = checkEntry->mValue;
  370. return true;
  371. }
  372. checkEntryIdx = checkEntry->mNext;
  373. }
  374. return false;
  375. }
  376. template <typename TKey>
  377. MatchIterator TryGet(const TKey& key)
  378. {
  379. if (mHashHeads == NULL)
  380. return end();
  381. this->CheckRehash();
  382. int hash = TFuncs::GetHash(key);
  383. int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
  384. int checkEntryIdx = this->mHashHeads[hashIdx];
  385. while (checkEntryIdx != -1)
  386. {
  387. auto checkEntry = &this->mEntries[checkEntryIdx];
  388. if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
  389. {
  390. MatchIterator itr(this, key);
  391. itr.mCurEntry = checkEntryIdx;
  392. itr.mCurBucket = hashIdx;
  393. return itr;
  394. }
  395. checkEntryIdx = checkEntry->mNext;
  396. }
  397. return end();
  398. }
  399. template <typename TKey>
  400. bool Contains(const TKey& key)
  401. {
  402. return TryGet(key) != end();
  403. }
  404. template <typename TKey>
  405. int GetCount(const TKey& key)
  406. {
  407. int count = 0;
  408. for (auto itr = TryGet(key); itr != end(); ++itr)
  409. count++;
  410. return count;
  411. }
  412. template <typename TKey>
  413. bool Remove(const TKey& key)
  414. {
  415. if (mHashHeads == NULL)
  416. return false;
  417. this->CheckRehash();
  418. int hash = TFuncs::GetHash(key);
  419. int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
  420. int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
  421. int checkEntryIdx = *srcCheckEntryPtr;
  422. while (checkEntryIdx != -1)
  423. {
  424. auto checkEntry = &mEntries[checkEntryIdx];
  425. if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
  426. {
  427. *srcCheckEntryPtr = checkEntry->mNext;
  428. FreeIdx(checkEntryIdx);
  429. return true;
  430. }
  431. srcCheckEntryPtr = &checkEntry->mNext;
  432. checkEntryIdx = checkEntry->mNext;
  433. }
  434. return false;
  435. }
  436. Iterator Erase(const Iterator& itr)
  437. {
  438. Iterator next = itr;
  439. ++next;
  440. bool found = false;
  441. auto entryIdx = itr.mCurEntry;
  442. auto entry = &mEntries[entryIdx];
  443. int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize;
  444. int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
  445. int checkEntryIdx = *srcCheckEntryPtr;
  446. while (checkEntryIdx != -1)
  447. {
  448. auto checkEntry = &mEntries[checkEntryIdx];
  449. if (checkEntryIdx == itr.mCurEntry)
  450. {
  451. *srcCheckEntryPtr = checkEntry->mNext;
  452. found = true;
  453. }
  454. srcCheckEntryPtr = &checkEntry->mNext;
  455. checkEntryIdx = checkEntry->mNext;
  456. }
  457. BF_ASSERT(found);
  458. FreeIdx(entryIdx);
  459. return next;
  460. }
  461. void Clear()
  462. {
  463. if (!TFuncs::deallocateAll())
  464. {
  465. auto itr = begin();
  466. auto endItr = end();
  467. while (itr != endItr)
  468. {
  469. auto entry = itr.mCurEntry;
  470. ++itr;
  471. FreeIdx(entry);
  472. }
  473. TFuncs::deallocate(this->mHashHeads);
  474. TFuncs::deallocate(this->mEntries);
  475. }
  476. this->mHashSize = cDefaultHashSize;
  477. this->mHashHeads = NULL;
  478. this->mEntries = NULL;
  479. this->mCount = 0;
  480. }
  481. Iterator begin()
  482. {
  483. return ++Iterator(this);
  484. }
  485. Iterator end()
  486. {
  487. Iterator itr(this);
  488. itr.mCurBucket = this->mHashSize;
  489. return itr;
  490. }
  491. };
  492. NS_BF_END;