Dictionary.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. #pragma once
  2. #include "Array.h"
  3. #include <unordered_map>
  4. #define NEW_DICTIONAY
  5. NS_BF_BEGIN;
  6. #ifdef NEW_DICTIONAY
  7. template <typename TKey, typename TValue, typename TAlloc = AllocatorCLib >
  8. class Dictionary : public TAlloc
  9. {
  10. public:
  11. typedef int int_cosize;
  12. typedef TKey key_type;
  13. typedef TValue value_type;
  14. struct EntryPair
  15. {
  16. public:
  17. TKey mKey; // Key of entry
  18. TValue mValue; // Value of entry
  19. };
  20. struct RawEntryPair
  21. {
  22. public:
  23. typename std::aligned_storage<sizeof(TKey), alignof(TKey)>::type mKey;
  24. typename std::aligned_storage<sizeof(TValue), alignof(TValue)>::type mValue;
  25. };
  26. public:
  27. struct Entry : public RawEntryPair
  28. {
  29. int_cosize mNext; // Index of next entry, -1 if last
  30. int32 mHashCode; // Lower 31 bits of hash code, -1 if unused
  31. };
  32. public:
  33. int_cosize* mBuckets;
  34. Entry* mEntries;
  35. int_cosize mAllocSize;
  36. int_cosize mCount;
  37. int_cosize mFreeList;
  38. int_cosize mFreeCount;
  39. public:
  40. struct iterator
  41. {
  42. public:
  43. typedef std::bidirectional_iterator_tag iterator_category;
  44. //typedef T value_type;
  45. //typedef intptr difference_type;
  46. //typedef T* pointer;
  47. //typedef T& reference;
  48. public:
  49. Dictionary* mDictionary;
  50. int_cosize mIdx;
  51. int_cosize mCurrentIdx;
  52. protected:
  53. void MoveNext()
  54. {
  55. while ((uintptr)mIdx < (uintptr)mDictionary->mCount)
  56. {
  57. if (mDictionary->mEntries[mIdx].mHashCode >= 0)
  58. {
  59. mCurrentIdx = mIdx;
  60. mIdx++;
  61. return;
  62. }
  63. mIdx++;
  64. }
  65. mIdx = mDictionary->mCount + 1;
  66. mCurrentIdx = -1;
  67. }
  68. public:
  69. iterator()
  70. {
  71. mDictionary = NULL;
  72. mIdx = 0;
  73. mCurrentIdx = -1;
  74. }
  75. iterator(Dictionary* dict, int idx)
  76. {
  77. mDictionary = dict;
  78. mIdx = idx;
  79. mCurrentIdx = idx;
  80. }
  81. iterator(Dictionary* dict, int idx, int currentIdx)
  82. {
  83. mDictionary = dict;
  84. mIdx = idx;
  85. mCurrentIdx = currentIdx;
  86. }
  87. iterator& operator++()
  88. {
  89. MoveNext();
  90. return *this;
  91. }
  92. iterator operator++(int)
  93. {
  94. auto prevVal = *this;
  95. MoveNext();
  96. return prevVal;
  97. }
  98. bool NextWithSameKey(const TKey& key)
  99. {
  100. int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
  101. while (mCurrentIdx >= 0)
  102. {
  103. mCurrentIdx = mDictionary->mEntries[mCurrentIdx].mNext;
  104. if (mCurrentIdx < 0)
  105. break;
  106. if ((mDictionary->mEntries[mCurrentIdx].mHashCode == hashCode) &&
  107. ((*(TKey*)&mDictionary->mEntries[mCurrentIdx].mKey) == key))
  108. {
  109. mIdx = mCurrentIdx;
  110. return true;
  111. }
  112. }
  113. mIdx = mDictionary->mCount + 1;
  114. mCurrentIdx = -1;
  115. return false;
  116. }
  117. bool operator!=(const iterator& itr) const
  118. {
  119. return (itr.mDictionary != mDictionary) || (itr.mIdx != mIdx);
  120. }
  121. bool operator==(const iterator& itr) const
  122. {
  123. return (itr.mDictionary == mDictionary) && (itr.mIdx == mIdx);
  124. }
  125. EntryPair& operator*()
  126. {
  127. return *(EntryPair*)&mDictionary->mEntries[mCurrentIdx];
  128. }
  129. EntryPair* operator->()
  130. {
  131. return (EntryPair*)&mDictionary->mEntries[mCurrentIdx];
  132. }
  133. };
  134. private:
  135. int_cosize GetPrimeish(int_cosize min)
  136. {
  137. // This is a minimal effort to help address-aligned dataa
  138. return (min | 1);
  139. }
  140. int_cosize ExpandSize(int_cosize oldSize)
  141. {
  142. int_cosize newSize = 2 * oldSize;
  143. // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
  144. // Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
  145. /*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
  146. {
  147. Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
  148. return MaxPrimeArrayLength;
  149. }*/
  150. return GetPrimeish(newSize);
  151. }
  152. void Resize()
  153. {
  154. Resize(ExpandSize(mCount), false);
  155. }
  156. void Resize(intptr newSize, bool forceNewHashCodes)
  157. {
  158. BF_ASSERT(newSize >= mAllocSize);
  159. Entry* newEntries;
  160. int_cosize* newBuckets;
  161. AllocData(newSize, newEntries, newBuckets);
  162. for (int_cosize i = 0; i < newSize; i++)
  163. newBuckets[i] = -1;
  164. for (int i = 0; i < mCount; i++)
  165. {
  166. auto& newEntry = newEntries[i];
  167. auto& oldEntry = mEntries[i];
  168. newEntry.mHashCode = oldEntry.mHashCode;
  169. newEntry.mNext = oldEntry.mNext;
  170. new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
  171. new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
  172. }
  173. for (int i = mCount; i < newSize; i++)
  174. {
  175. newEntries[i].mHashCode = -1;
  176. }
  177. if (forceNewHashCodes)
  178. {
  179. for (int_cosize i = 0; i < mCount; i++)
  180. {
  181. if (newEntries[i].mHashCode != -1)
  182. {
  183. newEntries[i].mHashCode = (int_cosize)BeefHash<TKey>()(*(TKey*)&newEntries[i].mKey) & 0x7FFFFFFF;
  184. }
  185. }
  186. }
  187. for (int_cosize i = 0; i < mCount; i++)
  188. {
  189. if (newEntries[i].mHashCode >= 0)
  190. {
  191. int_cosize bucket = (int_cosize)(newEntries[i].mHashCode % newSize);
  192. newEntries[i].mNext = newBuckets[bucket];
  193. newBuckets[bucket] = i;
  194. }
  195. }
  196. DeleteData();
  197. mBuckets = newBuckets;
  198. mEntries = newEntries;
  199. mAllocSize = (int_cosize)newSize;
  200. }
  201. int FindEntry(const TKey& key)
  202. {
  203. if (mBuckets != NULL)
  204. {
  205. int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
  206. for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
  207. {
  208. if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
  209. }
  210. }
  211. return -1;
  212. }
  213. template <typename TAltKey>
  214. int FindEntryWith(const TAltKey& key)
  215. {
  216. if (mBuckets != NULL)
  217. {
  218. int_cosize hashCode = (int_cosize)BeefHash<TAltKey>()(key) & 0x7FFFFFFF;
  219. for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
  220. {
  221. if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
  222. }
  223. }
  224. return -1;
  225. }
  226. void Initialize(intptr capacity)
  227. {
  228. int_cosize size = GetPrimeish((int_cosize)capacity);
  229. AllocData(size, mEntries, mBuckets);
  230. mAllocSize = size;
  231. for (int_cosize i = 0; i < (int_cosize)mAllocSize; i++) mBuckets[i] = -1;
  232. mFreeList = -1;
  233. }
  234. bool Insert(const TKey& key, bool forceAdd, TKey** keyPtr, TValue** valuePtr)
  235. {
  236. if (mBuckets == NULL) Initialize(0);
  237. int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
  238. int_cosize targetBucket = hashCode % (int_cosize)mAllocSize;
  239. if (!forceAdd)
  240. {
  241. for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
  242. {
  243. if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
  244. {
  245. if (keyPtr != NULL)
  246. *keyPtr = (TKey*)&mEntries[i].mKey;
  247. if (valuePtr != NULL)
  248. *valuePtr = (TValue*)&mEntries[i].mValue;
  249. return false;
  250. }
  251. }
  252. }
  253. int_cosize index;
  254. if (mFreeCount > 0)
  255. {
  256. index = mFreeList;
  257. mFreeList = mEntries[index].mNext;
  258. mFreeCount--;
  259. }
  260. else
  261. {
  262. if (mCount == mAllocSize)
  263. {
  264. Resize();
  265. targetBucket = hashCode % (int_cosize)mAllocSize;
  266. }
  267. index = mCount;
  268. mCount++;
  269. }
  270. mEntries[index].mHashCode = hashCode;
  271. mEntries[index].mNext = mBuckets[targetBucket];
  272. new (&mEntries[index].mKey) TKey(key);
  273. mBuckets[targetBucket] = index;
  274. if (keyPtr != NULL)
  275. *keyPtr = (TKey*)&mEntries[index].mKey;
  276. if (valuePtr != NULL)
  277. *valuePtr = (TValue*)&mEntries[index].mValue;
  278. return true;
  279. }
  280. void RemoveIdx(int_cosize bucket, int_cosize i, int_cosize last)
  281. {
  282. if (last < 0)
  283. {
  284. mBuckets[bucket] = mEntries[i].mNext;
  285. }
  286. else
  287. {
  288. mEntries[last].mNext = mEntries[i].mNext;
  289. }
  290. mEntries[i].mHashCode = -1;
  291. mEntries[i].mNext = mFreeList;
  292. ((TKey*)&mEntries[i].mKey)->~TKey();
  293. ((TValue*)&mEntries[i].mValue)->~TValue();
  294. mFreeList = i;
  295. mFreeCount++;
  296. }
  297. public:
  298. Dictionary()
  299. {
  300. mBuckets = NULL;
  301. mEntries = NULL;
  302. mAllocSize = 0;
  303. mCount = 0;
  304. mFreeList = 0;
  305. mFreeCount = 0;
  306. }
  307. Dictionary(const Dictionary& val)
  308. {
  309. mAllocSize = val.mAllocSize;
  310. mCount = val.mCount;
  311. if (mAllocSize == 0)
  312. {
  313. mBuckets = NULL;
  314. mEntries = NULL;
  315. }
  316. else
  317. {
  318. AllocData(mAllocSize, mEntries, mBuckets);
  319. for (int_cosize i = 0; i < mAllocSize; i++)
  320. mBuckets[i] = val.mBuckets[i];
  321. for (int i = 0; i < mCount; i++)
  322. {
  323. auto& newEntry = mEntries[i];
  324. auto& oldEntry = val.mEntries[i];
  325. newEntry.mHashCode = oldEntry.mHashCode;
  326. newEntry.mNext = oldEntry.mNext;
  327. new (&newEntry.mKey) TKey(*(TKey*)&oldEntry.mKey);
  328. new (&newEntry.mValue) TValue(*(TValue*)&oldEntry.mValue);
  329. }
  330. for (int i = mCount; i < mAllocSize; i++)
  331. {
  332. mEntries[i].mHashCode = -1;
  333. }
  334. }
  335. mFreeCount = val.mFreeCount;
  336. mFreeList = val.mFreeList;
  337. }
  338. Dictionary(Dictionary&& val)
  339. {
  340. mBuckets = val.mBuckets;
  341. mEntries = val.mEntries;
  342. mAllocSize = val.mAllocSize;
  343. mCount = val.mCount;
  344. mFreeList = val.mFreeList;
  345. mFreeCount = val.mFreeCount;
  346. val.mBuckets = NULL;
  347. val.mEntries = NULL;
  348. val.mAllocSize = 0;
  349. val.mCount = 0;
  350. val.mFreeList = 0;
  351. val.mFreeCount = 0;
  352. }
  353. ~Dictionary()
  354. {
  355. DeleteData();
  356. }
  357. void AllocData(intptr size, Entry*& outEntries, int_cosize*& outBuckets)
  358. {
  359. uint8* data = (uint8*)this->rawAllocate(size * (sizeof(Entry) + sizeof(int_cosize)));
  360. outEntries = (Entry*)data;
  361. outBuckets = (int_cosize*)(data + size * sizeof(Entry));
  362. }
  363. void DeleteData()
  364. {
  365. if (!std::is_pod<TKey>::value)
  366. {
  367. for (int_cosize i = 0; i < mCount; i++)
  368. {
  369. if (mEntries[i].mHashCode != -1)
  370. ((TKey*)&mEntries[i].mKey)->~TKey();
  371. }
  372. }
  373. if (!std::is_pod<TValue>::value)
  374. {
  375. for (int_cosize i = 0; i < mCount; i++)
  376. {
  377. if (mEntries[i].mHashCode != -1)
  378. ((TValue*)&mEntries[i].mValue)->~TValue();
  379. }
  380. }
  381. this->rawDeallocate(mEntries);
  382. }
  383. Dictionary& operator=(const Dictionary& rhs)
  384. {
  385. Clear();
  386. for (auto& kv : rhs)
  387. ForceAdd(kv.mKey, kv.mValue);
  388. return *this;
  389. }
  390. intptr GetCount() const
  391. {
  392. return mCount - mFreeCount;
  393. }
  394. intptr size() const
  395. {
  396. return mCount - mFreeCount;
  397. }
  398. bool IsEmpty() const
  399. {
  400. return mCount - mFreeCount == 0;
  401. }
  402. void Reserve(intptr size)
  403. {
  404. if (size > mAllocSize)
  405. Resize(size, false);
  406. }
  407. TValue& operator[](const TKey& key)
  408. {
  409. TValue* valuePtr;
  410. if (Insert(key, false, NULL, &valuePtr))
  411. {
  412. new (valuePtr) TValue();
  413. }
  414. return *valuePtr;
  415. }
  416. const TValue& operator[](const TKey& key) const
  417. {
  418. int_cosize i = (int_cosize)FindEntry(key);
  419. if (i >= 0)
  420. return mEntries[i].mValue;
  421. BF_FATAL("Key not found");
  422. return TValue();
  423. }
  424. void ForceAdd(const TKey& key, const TValue& value)
  425. {
  426. TValue* valuePtr = NULL;
  427. Insert(key, true, NULL, &valuePtr);
  428. new (valuePtr) TValue(value);
  429. }
  430. bool TryAdd(const TKey& key, const TValue& value)
  431. {
  432. TValue* valuePtr;
  433. if (!Insert(key, false, NULL, &valuePtr))
  434. return false;
  435. new (valuePtr) TValue(value);
  436. return true;
  437. }
  438. bool TryAdd(const TKey& key, TKey** keyPtr, TValue** valuePtr)
  439. {
  440. if (!Insert(key, false, keyPtr, valuePtr))
  441. return false;
  442. new (*valuePtr) TValue();
  443. return true;
  444. }
  445. // template <typename TAltKey>
  446. // bool TryAddWith(const TAltKey& key, TKey** keyPtr, TValue** valuePtr)
  447. // {
  448. // if (!Insert(key, false, keyPtr, valuePtr))
  449. // return false;
  450. // new (*valuePtr) TValue();
  451. // return true;
  452. // }
  453. // Returns uninitialized valuePtr - must use placement new
  454. bool TryAddRaw(const TKey& key, TKey** keyPtr, TValue** valuePtr)
  455. {
  456. return Insert(key, false, keyPtr, valuePtr);
  457. }
  458. bool TryGetValue(const TKey& key, TValue** valuePtr)
  459. {
  460. int_cosize i = (int_cosize)FindEntry(key);
  461. if (i >= 0)
  462. {
  463. *valuePtr = (TValue*)&mEntries[i].mValue;
  464. return true;
  465. }
  466. return false;
  467. }
  468. template <typename TAltKey>
  469. bool TryGetValueWith(const TAltKey& key, TValue** valuePtr)
  470. {
  471. int_cosize i = (int_cosize)FindEntryWith(key);
  472. if (i >= 0)
  473. {
  474. *valuePtr = (TValue*)&mEntries[i].mValue;
  475. return true;
  476. }
  477. return false;
  478. }
  479. bool TryGetValue(const TKey& key, TValue* valuePtr)
  480. {
  481. int_cosize i = (int_cosize)FindEntry(key);
  482. if (i >= 0)
  483. {
  484. *valuePtr = *(TValue*)&mEntries[i].mValue;
  485. return true;
  486. }
  487. return false;
  488. }
  489. iterator Find(const TKey& key)
  490. {
  491. int_cosize i = (int_cosize)FindEntry(key);
  492. iterator itr;
  493. if (i >= 0)
  494. {
  495. return iterator((Dictionary*)this, i);
  496. }
  497. else
  498. return iterator((Dictionary*)this, mCount + 1, -1);
  499. }
  500. bool Remove(const TKey& key)
  501. {
  502. if (mBuckets != NULL)
  503. {
  504. int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
  505. int_cosize bucket = hashCode % (int_cosize)mAllocSize;
  506. int_cosize last = -1;
  507. for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
  508. {
  509. if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
  510. {
  511. RemoveIdx(bucket, i, last);
  512. return true;
  513. }
  514. }
  515. }
  516. return false;
  517. }
  518. bool Remove(const TKey& key, TValue* valuePtr)
  519. {
  520. if (mBuckets != NULL)
  521. {
  522. int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
  523. int_cosize bucket = hashCode % (int_cosize)mAllocSize;
  524. int_cosize last = -1;
  525. for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
  526. {
  527. if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
  528. {
  529. *valuePtr = *(TValue*)&mEntries[i].mValue;
  530. RemoveIdx(bucket, i, last);
  531. return true;
  532. }
  533. }
  534. }
  535. return false;
  536. }
  537. bool Remove(const TKey& key, const TValue& value)
  538. {
  539. if (mBuckets != NULL)
  540. {
  541. int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
  542. int_cosize bucket = hashCode % (int_cosize)mAllocSize;
  543. int_cosize last = -1;
  544. for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
  545. {
  546. if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key) &&
  547. (*(TValue*)&mEntries[i].mValue == value))
  548. {
  549. RemoveIdx(bucket, i, last);
  550. return true;
  551. }
  552. }
  553. }
  554. return false;
  555. }
  556. iterator Remove(const iterator& itr)
  557. {
  558. iterator nextItr = itr;
  559. ++nextItr;
  560. auto& entry = mEntries[itr.mCurrentIdx];
  561. BF_ASSERT(entry.mHashCode >= 0);
  562. // We have to iterate through to mCurrentIdx so we can get 'last'
  563. int_cosize hashCode = entry.mHashCode;
  564. int_cosize bucket = hashCode % (int_cosize)mAllocSize;
  565. int_cosize last = -1;
  566. for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
  567. {
  568. if ((mEntries[i].mHashCode == hashCode) && (i == itr.mCurrentIdx))
  569. {
  570. RemoveIdx(bucket, i, last);
  571. return nextItr;
  572. }
  573. }
  574. BF_FATAL("not found");
  575. return nextItr;
  576. }
  577. void Clear()
  578. {
  579. if (mCount > 0)
  580. {
  581. for (int_cosize i = 0; i < mAllocSize; i++) mBuckets[i] = -1;
  582. if (!std::is_pod<TKey>::value)
  583. {
  584. for (int_cosize i = 0; i < mCount; i++)
  585. {
  586. if (mEntries[i].mHashCode != -1)
  587. ((TKey*)&mEntries[i].mKey)->~TKey();
  588. }
  589. }
  590. if (!std::is_pod<TValue>::value)
  591. {
  592. for (int_cosize i = 0; i < mCount; i++)
  593. {
  594. if (mEntries[i].mHashCode != -1)
  595. ((TValue*)&mEntries[i].mValue)->~TValue();
  596. }
  597. }
  598. for (int_cosize i = 0; i < mCount; i++)
  599. {
  600. mEntries[i].mHashCode = -1;
  601. }
  602. mFreeList = -1;
  603. mCount = 0;
  604. mFreeCount = 0;
  605. }
  606. }
  607. bool ContainsKey(const TKey& key)
  608. {
  609. return FindEntry(key) >= 0;
  610. }
  611. iterator begin() const
  612. {
  613. iterator itr((Dictionary*)this, 0);
  614. ++itr;
  615. return itr;
  616. }
  617. iterator end() const
  618. {
  619. return iterator((Dictionary*)this, mCount + 1, -1);
  620. }
  621. };
  622. #else
  623. template <typename TKey, typename TValue>
  624. class Dictionary : public std::unordered_map<TKey, TValue>
  625. {
  626. public:
  627. public:
  628. TValue* GetValuePtr(const TKey& key)
  629. {
  630. auto itr = find(key);
  631. if (itr == end())
  632. return NULL;
  633. return &itr->second;
  634. }
  635. bool ContainsKey(const TKey& key)
  636. {
  637. return find(key) != end();
  638. }
  639. };
  640. #endif
  641. NS_BF_END;