jhash.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #ifndef JHASH_HPP
  14. #define JHASH_HPP
  15. #include <functional>
  16. #include <unordered_map>
  17. #include <utility>
  18. #include "platform.h"
  19. #include <stdio.h>
  20. #include "jdebug.hpp" // for get_cycles_now()
  21. #include "jiface.hpp"
  22. #include "jobserve.hpp"
  23. #include "jiter.hpp"
  24. #include "jsuperhash.hpp"
  25. #ifndef IHASH_DEFINED // may be defined already elsewhere
  26. #define IHASH_DEFINED
  27. interface IHash
  28. {
  29. virtual unsigned hash(const void *data)=0;
  30. protected:
  31. virtual ~IHash() {}
  32. };
  33. #endif
  34. interface jlib_decl IMapping : extends IObservable
  35. {
  36. public:
  37. virtual const void * getKey() const = 0;
  38. virtual unsigned getHash() const = 0;
  39. virtual void setHash(unsigned) = 0;
  40. };
  41. interface jlib_decl IAtom : extends IMapping
  42. {
  43. public:
  44. virtual const char * queryStr() const = 0;
  45. };
  46. inline jlib_decl const char * str(const IAtom * atom) { return atom ? atom->queryStr() : NULL; }
  47. //This interface represents an atom which preserves its case, but also stores a lower case representation
  48. //for efficient case insensitive comparison.
  49. //It is deliberately NOT derived from IAtom to avoid accidentally using the wrong interface
  50. interface jlib_decl IIdAtom : extends IMapping
  51. {
  52. public:
  53. virtual const char * queryStr() const = 0;
  54. public:
  55. virtual IAtom * queryLower() const = 0;
  56. };
  57. inline jlib_decl const char * str(const IIdAtom * atom) { return atom ? atom->queryStr() : NULL; }
  58. inline jlib_decl IAtom * lower(const IIdAtom * atom) { return atom ? atom->queryLower() : NULL; }
  59. #ifdef _MSC_VER
  60. #pragma warning (push)
  61. #pragma warning( disable : 4275 )
  62. #endif
  63. class jlib_decl HashTable
  64. : public SuperHashTableOf<IMapping, const void>, implements IObserver
  65. {
  66. public:
  67. HashTable(int _keysize, bool _ignorecase)
  68. : SuperHashTableOf<IMapping, const void>(),
  69. keysize(_keysize), ignorecase(_ignorecase) {}
  70. HashTable(unsigned initsize, int _keysize, bool _ignorecase)
  71. : SuperHashTableOf<IMapping, const void>(initsize),
  72. keysize(_keysize), ignorecase(_ignorecase) {}
  73. ~HashTable() { _releaseAll(); }
  74. IMPLEMENT_IINTERFACE
  75. bool add(IMapping & donor)
  76. {
  77. donor.setHash(hash(donor.getKey(), keysize));
  78. return SuperHashTableOf<IMapping, const void>::add(donor);
  79. }
  80. bool replace(IMapping & donor)
  81. {
  82. donor.setHash(hash(donor.getKey(), keysize));
  83. return SuperHashTableOf<IMapping, const void>::replace(donor);
  84. }
  85. bool addOwn(IMapping & donor);
  86. bool replaceOwn(IMapping & donor);
  87. IMapping * findLink(const void *key) const;
  88. virtual bool onNotify(INotification & notify);
  89. IMapping * createLink(const void *key);
  90. protected:
  91. virtual IMapping * newMapping(const void *key);
  92. private:
  93. unsigned getHashFromElement(const void * et) const
  94. { return static_cast<const IMapping *>(et)->getHash(); }
  95. unsigned getHashFromFindParam(const void *fp) const
  96. { return hash(fp, keysize); }
  97. const void * getFindParam(const void * et) const
  98. { return const_cast<const void *>(static_cast<const IMapping *>(et)->getKey()); }
  99. bool matchesFindParam(const void * et, const void * key, unsigned fphash __attribute__((unused))) const
  100. { return keyeq(key, static_cast<const IMapping *>(et)->getKey(), keysize); }
  101. bool keyeq(const void *key1, const void *key2, int ksize) const;
  102. unsigned hash(const void *key, int ksize) const;
  103. private:
  104. int keysize;
  105. bool ignorecase;
  106. };
  107. class jlib_decl KeptHashTable : public HashTable
  108. {
  109. public:
  110. KeptHashTable(int _keysize, bool _ignorecase)
  111. : HashTable(_keysize, _ignorecase) {}
  112. KeptHashTable(unsigned _initsize, int _keysize, bool _ignorecase)
  113. : HashTable(_initsize, _keysize, _ignorecase) {}
  114. ~KeptHashTable() { _releaseAll(); }
  115. IMapping * create(const void *key);
  116. private:
  117. void onAdd(void *et);
  118. void onRemove(void *et);
  119. };
  120. class jlib_decl ObservedHashTable : public HashTable
  121. {
  122. public:
  123. ObservedHashTable(int _keysize, bool _ignorecase)
  124. : HashTable(_keysize, _ignorecase) {}
  125. ObservedHashTable(unsigned _initsize, int _keysize, bool _ignorecase)
  126. : HashTable(_initsize, _keysize, _ignorecase) {}
  127. ~ObservedHashTable() { _releaseAll(); }
  128. private:
  129. void onAdd(void *et);
  130. void onRemove(void *et);
  131. };
  132. class jlib_decl HashIterator : public SuperHashIteratorOf<IMapping>
  133. {
  134. public:
  135. HashIterator(const HashTable & _table) : SuperHashIteratorOf<IMapping>(_table) {}
  136. IMapping & get() { IMapping & et = query(); et.Link(); return et; }
  137. };
  138. #ifdef _MSC_VER
  139. #pragma warning (pop)
  140. #endif
  141. void IntersectHash(HashTable & h1, HashTable & h2);
  142. void UnionHash(HashTable & h1, HashTable & h2);
  143. void SubtractHash(HashTable & main, HashTable & sub);
  144. #include "jhash.ipp"
  145. typedef MappingStringTo<IInterfacePtr,IInterfacePtr> CopyMappingStringToIInterface;
  146. typedef MapStringTo<IInterfacePtr,IInterfacePtr,CopyMappingStringToIInterface> CopyMapStringToIInterface;
  147. template <class C> class CopyMapStringToMyClass : public CopyMapStringToIInterface
  148. {
  149. public:
  150. CopyMapStringToMyClass() : CopyMapStringToIInterface() {};
  151. CopyMapStringToMyClass(bool _ignorecase) : CopyMapStringToIInterface(_ignorecase) {};
  152. static inline C * mapToValue(IMapping * _map)
  153. {
  154. CopyMappingStringToIInterface * map = (CopyMappingStringToIInterface *)_map;
  155. IInterface ** x = &map->getValue();
  156. return x ? (C *) *x : NULL;
  157. }
  158. C *getValue(const char *k) const
  159. {
  160. IInterface **x = CopyMapStringToIInterface::getValue(k);
  161. return x ? (C *) *x : NULL;
  162. }
  163. };
  164. template <class C, class BASE> class CopyMapStringToMyClassViaBase : public CopyMapStringToIInterface
  165. {
  166. public:
  167. CopyMapStringToMyClassViaBase() : CopyMapStringToIInterface() {};
  168. CopyMapStringToMyClassViaBase(bool _ignorecase) : CopyMapStringToIInterface(_ignorecase) {};
  169. C *getValue(const char *k) const
  170. {
  171. IInterface **x = CopyMapStringToIInterface::getValue(k);
  172. return x ? (C *)(BASE *)*x : NULL;
  173. }
  174. bool setValue(const char *k, C * v)
  175. {
  176. return CopyMapStringToIInterface::setValue(k, (BASE *)v);
  177. }
  178. };
  179. //===========================================================================
  180. #ifdef _MSC_VER
  181. #pragma warning (push)
  182. #pragma warning( disable : 4275 ) // hope this warning not significant! (may get link errors I guess)
  183. #endif
  184. class jlib_decl MappingStringToIInterface : public MappingStringTo<IInterfacePtr,IInterfacePtr>
  185. {
  186. public:
  187. MappingStringToIInterface(const char * k, IInterfacePtr a) :
  188. MappingStringTo<IInterfacePtr,IInterfacePtr>(k,a) { ::Link(a); }
  189. ~MappingStringToIInterface() { ::Release(val); }
  190. };
  191. #ifdef _MSC_VER
  192. #pragma warning (pop)
  193. #endif
  194. typedef MapStringTo<IInterfacePtr,IInterfacePtr,MappingStringToIInterface> MapStringToIInterface;
  195. template <class C> class MapStringToMyClass : public MapStringToIInterface
  196. {
  197. public:
  198. MapStringToMyClass() : MapStringToIInterface() {};
  199. MapStringToMyClass(bool _ignorecase) : MapStringToIInterface(_ignorecase) {};
  200. static inline C * mapToValue(IMapping * _map)
  201. {
  202. MappingStringToIInterface * map = (MappingStringToIInterface *)_map;
  203. IInterface ** x = &map->getValue();
  204. return x ? (C *) *x : NULL;
  205. }
  206. C *getValue(const char *k) const
  207. {
  208. IInterface **x = MapStringToIInterface::getValue(k);
  209. return x ? (C *) *x : NULL;
  210. }
  211. };
  212. template <class C, class BASE> class MapStringToMyClassViaBase : public MapStringToIInterface
  213. {
  214. public:
  215. MapStringToMyClassViaBase() : MapStringToIInterface() {};
  216. MapStringToMyClassViaBase(bool _ignorecase) : MapStringToIInterface(_ignorecase) {};
  217. C *getValue(const char *k) const
  218. {
  219. IInterface **x = MapStringToIInterface::getValue(k);
  220. return x ? (C *)(BASE *)*x : NULL;
  221. }
  222. bool setValue(const char *k, C * v)
  223. {
  224. return MapStringToIInterface::setValue(k, (BASE *)v);
  225. }
  226. };
  227. //===========================================================================
  228. template <class KEY, class KEYINIT>
  229. class CopyMapXToIInterface : public MapBetween<KEY, KEYINIT, IInterfacePtr,IInterfacePtr,MappingBetween<KEY, KEYINIT, IInterfacePtr, IInterfacePtr> >
  230. {
  231. };
  232. template <class KEY, class KEYINIT, class C>
  233. class CopyMapXToMyClass : public CopyMapXToIInterface<KEY, KEYINIT>
  234. {
  235. public:
  236. CopyMapXToMyClass() : CopyMapXToIInterface<KEY, KEYINIT>() {};
  237. static inline C * mapToValue(IMapping * _map)
  238. {
  239. MappingBetween<KEY, KEYINIT, IInterfacePtr, IInterfacePtr> * map = (MappingBetween<KEY, KEYINIT, IInterfacePtr, IInterfacePtr> *)_map;
  240. IInterface ** x = &map->getValue();
  241. return x ? (C *) *x : NULL;
  242. }
  243. C *getValue(KEYINIT k) const
  244. {
  245. IInterface **x = CopyMapXToIInterface<KEY, KEYINIT>::getValue(k);
  246. return x ? (C *) *x : NULL;
  247. }
  248. };
  249. template <class KEY, class KEYINIT>
  250. class MappingXToIInterface : public MappingBetween<KEY, KEYINIT, IInterfacePtr,IInterfacePtr>
  251. {
  252. typedef MappingXToIInterface<KEY, KEYINIT> SELF;
  253. public:
  254. MappingXToIInterface(KEYINIT k, IInterfacePtr a) :
  255. MappingBetween<KEY, KEYINIT, IInterfacePtr,IInterfacePtr>(k,a) { ::Link(a); }
  256. ~MappingXToIInterface() { ::Release(SELF::val); }
  257. };
  258. template <class KEY, class KEYINIT>
  259. class MapXToIInterface : public MapBetween<KEY, KEYINIT, IInterfacePtr,IInterfacePtr,MappingXToIInterface<KEY, KEYINIT> >
  260. {
  261. public:
  262. using ELEMENT = MappingXToIInterface<KEY, KEYINIT>;
  263. };
  264. template <class KEY, class KEYINIT, class C>
  265. class MapXToMyClass : public MapXToIInterface<KEY, KEYINIT>
  266. {
  267. public:
  268. MapXToMyClass() : MapXToIInterface<KEY, KEYINIT>() {};
  269. static inline C * mapToValue(IMapping * _map)
  270. {
  271. MappingXToIInterface<KEY, KEYINIT> * map = (MappingXToIInterface<KEY, KEYINIT> *)_map;
  272. IInterface ** x = &map->getValue();
  273. return x ? (C *) *x : NULL;
  274. }
  275. C *getValue(KEYINIT k) const
  276. {
  277. IInterface **x = MapXToIInterface<KEY, KEYINIT>::getValue(k);
  278. return x ? (C *) *x : NULL;
  279. }
  280. };
  281. template <class KEY, class KEYINIT, class C, class BASE>
  282. class MapXToMyClassViaBase : public MapXToIInterface<KEY, KEYINIT>
  283. {
  284. public:
  285. MapXToMyClassViaBase () : MapXToIInterface<KEY, KEYINIT>() {};
  286. static inline C * mapToValue(IMapping * _map)
  287. {
  288. MappingXToIInterface<KEY, KEYINIT> * map = (MappingXToIInterface<KEY, KEYINIT> *)_map;
  289. IInterface ** x = &map->getValue();
  290. return x ? (C *)(BASE *)*x : NULL;
  291. }
  292. C *getValue(KEYINIT k) const
  293. {
  294. IInterface **x = MapXToIInterface<KEY, KEYINIT>::getValue(k);
  295. return x ? (C *)(BASE *) *x : NULL;
  296. }
  297. bool setValue(KEYINIT k, BASE * v)
  298. {
  299. return MapXToIInterface<KEY, KEYINIT>::setValue(k, v);
  300. }
  301. };
  302. //===========================================================================
  303. template <class KEY, class VALUE>
  304. class MappingOwnedToOwned : public MappingBetween<Linked<KEY>, KEY *, Linked<VALUE>,VALUE *>
  305. {
  306. public:
  307. MappingOwnedToOwned(KEY * k, VALUE * a) : MappingBetween<Linked<KEY>, KEY *, Linked<VALUE>,VALUE *>(k, a) {}
  308. };
  309. template <class KEY, class VALUE>
  310. class MapOwnedToOwned : public MapBetween<Linked<KEY>, KEY * , Linked<VALUE>,VALUE *,MappingOwnedToOwned<KEY, VALUE> >
  311. {
  312. public:
  313. inline MapOwnedToOwned() {}
  314. inline MapOwnedToOwned(unsigned initsize) : MapBetween<Linked<KEY>, KEY * , Linked<VALUE>,VALUE *,MappingOwnedToOwned<KEY, VALUE> >(initsize) {}
  315. };
  316. /*
  317. The hash tables can be used from the interfaces above. However there are
  318. some helper classes/macros in jhash.ipp to make them easier to use:
  319. Element classes:
  320. HashMapping - An implementation of IMapping that works in keep and
  321. non kept hash tables.
  322. Atom - An implementation of IAtom
  323. MappingKey - A class for storing a hash key - used by above.
  324. There are also some classes to make creating hash tables easier:
  325. (They are actually implemented as macros to stop the compiler choking on
  326. the template classes).
  327. a) To create a hash table of a given class use:
  328. typedef HashTableOf<MAPPING-TYPE, HASH-KEY-SIZE> MapHashTable;
  329. Using a HashTableOf also adds the following method to the hash table class
  330. MAPPING-TYPE * CreateLink(const void * key);
  331. which creates entries in the hash table.
  332. b) To create a hash table that maps a string to another type use:
  333. typedef MapStringTo<TO-TYPE, TO-INIT> MyMappingName;
  334. where
  335. TO-TYPE - what is stored in the mapping
  336. TO-INIT - what you pass to the value to initialise it.
  337. e.g.
  338. typedef MapStringTo<StringAttr, const char *> MapStrToStr;
  339. c) To create a hash table that maps a non-string to another type use:
  340. typedef MapBetween<KEY-TYPE, KEY-INIT, MAP-TYPE, MAP-INIT> myMap;
  341. where MAP-... as above and
  342. KEY-TYPE - what is stored as the key
  343. KEY-INIT - what you pass to the key to initialise it.
  344. e.g.,
  345. typedef MapBetween<int, int, StringAttr, const char *> MapIntToStr;
  346. *** HashTable Key sizes ***
  347. The HashTable key size can have one of the following forms:
  348. +n - The key is n bytes of data
  349. 0 - The key is a null terminated string
  350. -n - The key is n bytes of data followed by a null terminated string.
  351. */
  352. // Create an atom in the global atom table
  353. extern jlib_decl IAtom * createAtom(const char *value);
  354. extern jlib_decl IAtom * createAtom(const char *value, size32_t len);
  355. inline IAtom * createLowerCaseAtom(const char *value) { return createAtom(value); }
  356. inline IAtom * createLowerCaseAtom(const char *value, size32_t len) { return createAtom(value, len); }
  357. extern jlib_decl IIdAtom * createIdAtom(const char *value);
  358. extern jlib_decl IIdAtom * createIdAtom(const char *value, size32_t len);
  359. extern jlib_decl void releaseAtoms();
  360. extern jlib_decl unsigned hashc( const unsigned char *k, unsigned length, unsigned initval);
  361. extern jlib_decl unsigned hashnc( const unsigned char *k, unsigned length, unsigned initval);
  362. extern jlib_decl unsigned hashvalue( unsigned value, unsigned initval);
  363. extern jlib_decl unsigned hashvalue( unsigned __int64 value, unsigned initval);
  364. extern jlib_decl unsigned hashvalue( const void * value, unsigned initval);
  365. //================================================
  366. // Minimal Hash table template - slightly less overhead that HashTable/SuperHashTable
  367. template <class C> class CMinHashTable
  368. {
  369. protected:
  370. unsigned htn;
  371. unsigned n;
  372. C **table;
  373. void expand(bool expand=true)
  374. {
  375. C **t = table+htn; // more interesting going backwards
  376. if (expand)
  377. htn = htn*2+1;
  378. else
  379. htn /= 2;
  380. C **newtable = (C **)calloc(sizeof(C *),htn);
  381. while (t--!=table) {
  382. C *c = *t;
  383. if (c) {
  384. unsigned h = c->hash%htn;
  385. while (newtable[h]) {
  386. h++;
  387. if (h==htn)
  388. h = 0;
  389. }
  390. newtable[h] = c;
  391. }
  392. }
  393. free(table);
  394. table = newtable;
  395. }
  396. public:
  397. CMinHashTable<C>(unsigned _initialSize = 7)
  398. {
  399. htn = _initialSize;
  400. n = 0;
  401. table = (C **)calloc(sizeof(C *),htn);
  402. }
  403. ~CMinHashTable<C>()
  404. {
  405. C **t = table+htn;
  406. while (t--!=table)
  407. if (*t)
  408. C::destroy(*t);
  409. free(table);
  410. }
  411. void add(C *c)
  412. {
  413. if (n*4>htn*3)
  414. expand();
  415. unsigned i = c->hash%htn;
  416. while (table[i])
  417. if (++i==htn)
  418. i = 0;
  419. table[i] = c;
  420. n++;
  421. }
  422. C *findh(const char *key,unsigned h)
  423. {
  424. unsigned i=h%htn;
  425. while (table[i]) {
  426. if ((table[i]->hash==h)&&table[i]->eq(key))
  427. return table[i];
  428. if (++i==htn)
  429. i = 0;
  430. }
  431. return NULL;
  432. }
  433. C *find(const char *key,bool add)
  434. {
  435. unsigned h = C::getHash(key);
  436. unsigned i=h%htn;
  437. while (table[i]) {
  438. if ((table[i]->hash==h)&&table[i]->eq(key))
  439. return table[i];
  440. if (++i==htn)
  441. i = 0;
  442. }
  443. if (!add)
  444. return NULL;
  445. C *c = C::create(key);
  446. table[i] = c;
  447. n++;
  448. if (n*4>htn*3)
  449. expand();
  450. return c;
  451. }
  452. unsigned findIndex(const char *key, unsigned h)
  453. {
  454. unsigned i=h%htn;
  455. while (table[i]) {
  456. if ((table[i]->hash==h)&&table[i]->eq(key))
  457. return i;
  458. if (++i==htn)
  459. i = 0;
  460. }
  461. return (unsigned) -1;
  462. }
  463. C *getIndex(unsigned v) const
  464. {
  465. assert(v != (unsigned)-1 && v < htn);
  466. return table[v];
  467. }
  468. void remove(C *c)
  469. {
  470. unsigned i = c->hash%htn;
  471. C::destroy(c);
  472. while (table[i]!=c) {
  473. if (table[i]==NULL) {
  474. return;
  475. }
  476. if (++i==htn)
  477. i = 0;
  478. }
  479. unsigned j = i+1;
  480. for (;;) {
  481. if (j==htn)
  482. j = 0;
  483. C *cn = table[j];
  484. if (cn==NULL)
  485. break;
  486. unsigned k = cn->hash%htn;
  487. if (j>i) {
  488. if ((k<=i)||(k>j)) {
  489. table[i] = cn;
  490. i = j;
  491. }
  492. }
  493. else if ((k>j)&&(k<=i)) {
  494. table[i] = cn;
  495. i = j;
  496. }
  497. j++;
  498. }
  499. table[i] = NULL;
  500. n--;
  501. if ((n>1024)&&(n<htn/4))
  502. expand(false);
  503. }
  504. C *first(unsigned &i)
  505. {
  506. i = 0;
  507. return next(i);
  508. }
  509. C *next(unsigned &i)
  510. {
  511. while (i!=htn) {
  512. C* r = table[i++];
  513. if (r)
  514. return r;
  515. }
  516. return NULL;
  517. }
  518. unsigned ordinality()
  519. {
  520. return n;
  521. }
  522. };
  523. /*
  524. * A HT/Cache implementation whose items are only valid for a defined timeout period (default 30 secs)
  525. * Example use:
  526. * CTimeLimitedCache<std::string, Owned<IPropertyTree>> myCache(10);
  527. * Owned<IPropertyTree> match;
  528. * verifyex( !myCache.get("tree1", match) );
  529. * myCache.add("tree1", createPTree("tree1"));
  530. * verifyex( nullptr != myCache.query("tree1") );
  531. * MilliSleep(11000); // sleep until "tree1" has expired
  532. * verifyex( nullptr == myCache.query("tree1") );
  533. *
  534. * myCache.ensure("tree2", [](std::string key) { return createPTree(key.c_str()); });
  535. *
  536. * Keywords: cache,time,limit,hashtable,hash
  537. */
  538. template <class KEYTYPE, class VALUETYPE>
  539. class CTimeLimitedCache
  540. {
  541. public:
  542. CTimeLimitedCache<KEYTYPE, VALUETYPE>(unsigned timeoutMs=defaultCacheTimeoutMs)
  543. {
  544. timeoutPeriodCycles = ((cycle_t)timeoutMs) * queryOneSecCycles() / 1000;
  545. }
  546. VALUETYPE *query(KEYTYPE key, bool touch=false)
  547. {
  548. CacheElement *match = getMatch(key, touch);
  549. if (!match)
  550. return nullptr;
  551. return &match->second;
  552. }
  553. bool get(KEYTYPE key, VALUETYPE &result, bool touch=false)
  554. {
  555. VALUETYPE *res = query(key, touch);
  556. if (!res)
  557. return false;
  558. result = *res;
  559. return true;
  560. }
  561. VALUETYPE &add(KEYTYPE key, VALUETYPE val)
  562. {
  563. auto &ref = ht[key];
  564. ref = std::make_pair(get_cycles_now(), val);
  565. return ref.second;
  566. }
  567. VALUETYPE &ensure(KEYTYPE key, std::function<VALUETYPE (KEYTYPE k)> func)
  568. {
  569. VALUETYPE *res = query(key);
  570. if (res)
  571. return *res;
  572. return add(key, func(key));
  573. }
  574. void kill()
  575. {
  576. // NB: std::unordered_map clear() does not free the map memory (or call dtors) until it is out of scope
  577. std::unordered_map<KEYTYPE, CacheElement> empty;
  578. empty.swap(ht);
  579. }
  580. private:
  581. static constexpr unsigned defaultCacheTimeoutMs = 30000;
  582. typedef std::pair<cycle_t, VALUETYPE> CacheElement;
  583. cycle_t timeoutPeriodCycles = 0;
  584. std::unordered_map<KEYTYPE, CacheElement> ht;
  585. CacheElement *getMatch(KEYTYPE key, bool touch)
  586. {
  587. auto it = ht.find(key);
  588. if (it == ht.end())
  589. return nullptr;
  590. cycle_t nowCycles = get_cycles_now();
  591. if ((nowCycles - it->second.first) > timeoutPeriodCycles) // NB: rollover is okay
  592. {
  593. ht.erase(it);
  594. return nullptr;
  595. }
  596. if (touch)
  597. it->second.first = nowCycles;
  598. return &it->second;
  599. }
  600. };
  601. #endif