A collection of basic/generally desirable code I use across multiple C++ projects.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

720 lines
23 KiB

1 year ago
  1. #ifndef TABLE_H
  2. #define TABLE_H
  3. #include <new> // new
  4. #include <functional> // std::function for traversal
  5. #include <type_traits> // std::enable_if
  6. #include "alloc.h"
  7. #include "string.h"
  8. #include "types.h"
  9. // what follows is a collection of hash functions taken from: https://www.partow.net/programming/hashfunctions/#:~:text=The%20hash%20functions%20in%20this,containers%20such%20as%20hash%2Dtables.
  10. //
  11. // Available Hash Functions
  12. // The General Hash Functions Library has the following mix of additive and rotative general purpose string hashing algorithms. The following algorithms vary in usefulness and functionality and are mainly intended as an example for learning how hash functions operate and what they basically look like in code form.
  13. //
  14. // 00 - RS Hash Function
  15. // A simple hash function from Robert Sedgwicks Algorithms in C book. I've added some simple optimizations to the algorithm in order to speed up its hashing process.
  16. #if 1
  17. static unsigned int RSHash(const char* str, unsigned int length)
  18. {
  19. unsigned int b = 378551;
  20. unsigned int a = 63689;
  21. unsigned int hash = 0;
  22. unsigned int i = 0;
  23. for (i = 0; i < length; ++str, ++i)
  24. {
  25. hash = hash * a + (*str);
  26. a = a * b;
  27. }
  28. return hash;
  29. }
  30. #endif
  31. // 01 - JS Hash Function
  32. // A bitwise hash function written by Justin Sobel
  33. #if 1
  34. static unsigned int JSHash(const char* str, unsigned int length)
  35. {
  36. unsigned int hash = 1315423911;
  37. unsigned int i = 0;
  38. for (i = 0; i < length; ++str, ++i)
  39. {
  40. hash ^= ((hash << 5) + (*str) + (hash >> 2));
  41. }
  42. return hash;
  43. }
  44. #endif
  45. // 02 - PJW Hash Function
  46. // This hash algorithm is based on work by Peter J. Weinberger of Renaissance Technologies. The book Compilers (Principles, Techniques and Tools) by Aho, Sethi and Ulman, recommends the use of hash functions that employ the hashing methodology found in this particular algorithm.
  47. #if 1
  48. static unsigned int PJWHash(const char* str, unsigned int length)
  49. {
  50. const unsigned int BitsInUnsignedInt = (unsigned int)(sizeof(unsigned int) * 8);
  51. const unsigned int ThreeQuarters = (unsigned int)((BitsInUnsignedInt * 3) / 4);
  52. const unsigned int OneEighth = (unsigned int)(BitsInUnsignedInt / 8);
  53. const unsigned int HighBits =
  54. (unsigned int)(0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);
  55. unsigned int hash = 0;
  56. unsigned int test = 0;
  57. unsigned int i = 0;
  58. for (i = 0; i < length; ++str, ++i)
  59. {
  60. hash = (hash << OneEighth) + (*str);
  61. if ((test = hash & HighBits) != 0)
  62. {
  63. hash = (( hash ^ (test >> ThreeQuarters)) & (~HighBits));
  64. }
  65. }
  66. return hash;
  67. }
  68. #endif
  69. // 03 - ELF Hash Function
  70. // Similar to the PJW Hash function, but tweaked for 32-bit processors. It is a widley used hash function on UNIX based systems.
  71. #if 1
  72. static unsigned int ELFHash(const char* str, unsigned int length)
  73. {
  74. unsigned int hash = 0;
  75. unsigned int x = 0;
  76. unsigned int i = 0;
  77. for (i = 0; i < length; ++str, ++i)
  78. {
  79. hash = (hash << 4) + (*str);
  80. if ((x = hash & 0xF0000000L) != 0)
  81. {
  82. hash ^= (x >> 24);
  83. }
  84. hash &= ~x;
  85. }
  86. return hash;
  87. }
  88. #endif
  89. // 04 - BKDR Hash Function
  90. // This hash function comes from Brian Kernighan and Dennis Ritchie's book "The C Programming Language". It is a simple hash function using a strange set of possible seeds which all constitute a pattern of 31....31...31 etc, it seems to be very similar to the DJB hash function.
  91. //
  92. // @NOTE (nick) - this is basically the one I was using before I found this resource with all these other hash functions. (I took it from K&R)
  93. // except, the seed in the version I used was '31', not '131'.
  94. #if 1
  95. static unsigned int BKDRHash(const char* str, unsigned int length)
  96. {
  97. unsigned int seed = 131; /* 31 131 1313 13131 131313 etc.. */
  98. unsigned int hash = 0;
  99. unsigned int i = 0;
  100. for (i = 0; i < length; ++str, ++i)
  101. {
  102. hash = (hash * seed) + (*str);
  103. }
  104. return hash;
  105. }
  106. #endif
  107. // 05 - SDBM Hash Function
  108. // This is the algorithm of choice which is used in the open source SDBM project. The hash function seems to have a good over-all distribution for many different data sets. It seems to work well in situations where there is a high variance in the MSBs of the elements in a data set.
  109. #if 1
  110. static unsigned int SDBMHash(const char* str, unsigned int length)
  111. {
  112. unsigned int hash = 0;
  113. unsigned int i = 0;
  114. for (i = 0; i < length; ++str, ++i)
  115. {
  116. hash = (*str) + (hash << 6) + (hash << 16) - hash;
  117. }
  118. return hash;
  119. }
  120. #endif
  121. // 06 - DJB Hash Function
  122. // An algorithm produced by Professor Daniel J. Bernstein and shown first to the world on the usenet newsgroup comp.lang.c. It is one of the most efficient hash functions ever published.
  123. #if 1
  124. static unsigned int DJBHash(const char* str, unsigned int length)
  125. {
  126. unsigned int hash = 5381;
  127. unsigned int i = 0;
  128. for (i = 0; i < length; ++str, ++i)
  129. {
  130. hash = ((hash << 5) + hash) + (*str);
  131. }
  132. return hash;
  133. }
  134. #endif
  135. // 07 - DEK Hash Function
  136. // An algorithm proposed by Donald E. Knuth in The Art Of Computer Programming Volume 3, under the topic of sorting and search chapter 6.4.
  137. #if 1
  138. static unsigned int DEKHash(const char* str, unsigned int length)
  139. {
  140. unsigned int hash = length;
  141. unsigned int i = 0;
  142. for (i = 0; i < length; ++str, ++i)
  143. {
  144. hash = ((hash << 5) ^ (hash >> 27)) ^ (*str);
  145. }
  146. return hash;
  147. }
  148. #endif
  149. // 08 - AP Hash Functionhing The Project Gutenberg Etext of Webster's Unabridged Dictionary, the longest encountered chain length was 7, the average chain length was 2, the number of empty buckets was 4579.
  150. // An algorithm produced by me Arash Partow. I took ideas from all of the above hash functions making a hybrid rotative and additive hash function algorithm. There isn't any real mathematical analysis explaining why one should use this hash function instead of the others described above other than the fact that I tired to resemble the design as close as possible to a simple LFSR. An empirical result which demonstrated the distributive abilities of the hash algorithm was obtained using a hash-table with 100003 buckets, hashing The Project Gutenberg Etext of Webster's Unabridged Dictionary, the longest encountered chain length was 7, the average chain length was 2, the number of empty buckets was 4579.
  151. // @NOTE(nick) - given the test case above of the whole english dictionary, I think this one makes sense for a general purpose, unknown-length key hash table.
  152. #if 1
  153. static unsigned int APHash(const char* str, unsigned int length)
  154. {
  155. unsigned int hash = 0xAAAAAAAA;
  156. unsigned int i = 0;
  157. for (i = 0; i < length; ++str, ++i)
  158. {
  159. hash ^= ((i & 1) == 0) ? ( (hash << 7) ^ (*str) * (hash >> 3)) :
  160. (~((hash << 11) + ((*str) ^ (hash >> 5))));
  161. }
  162. return hash;
  163. }
  164. #endif
  165. static inline u32 fastModuloReductionDanielLemire(u32 v, u32 c) {
  166. return (((u64)v) * ((u64)c)) >> 32;
  167. }
  168. static inline u32 hash(const char* key, u32 keyLength, u32 capacity) {
  169. TYPES_H_FTAG;
  170. u32 value = APHash(key, keyLength);
  171. return fastModuloReductionDanielLemire(value, capacity);
  172. }
  173. template <typename V>
  174. struct TableEntry {
  175. TableEntry<V>* next;
  176. const char* key;
  177. u32 keyLength;
  178. V value;
  179. };
  180. template <typename T>
  181. static void serialize(String* str, TableEntry<T>* entry) {
  182. serialize(str, entry->key);
  183. serialize(str, entry->keyLength);
  184. serialize(str, entry->value);
  185. }
  186. template <typename V>
  187. struct Table {
  188. u32 lanes;
  189. u32 length;
  190. TableEntry<V>** entries;
  191. Table<V>(u32 _lanes = 16) {
  192. TYPES_H_FTAG;
  193. this->lanes = _lanes;
  194. this->length = 0;
  195. this->entries = (TableEntry<V>**) pCalloc(sizeof(TableEntry<V>*), this->lanes);
  196. }
  197. void* operator new(size_t size) {
  198. TYPES_H_FTAG;
  199. return (Table<V>*) pMalloc(sizeof(Table<V>));
  200. }
  201. V insert(const char* key, u32 keyLength, V value) {
  202. TYPES_H_FTAG;
  203. TableEntry<V>* entry = this->lookup(key, keyLength);
  204. if (!entry) { // no entry with that key exists
  205. entry = (TableEntry<V>*) pMalloc(sizeof (TableEntry<V>));
  206. entry->key = String::cpy(key, keyLength);
  207. entry->keyLength = keyLength;
  208. entry->value = value;
  209. u32 hashValue = hash(key, keyLength, lanes);
  210. entry->next = entries[hashValue];
  211. entries[hashValue] = entry;
  212. this->length++;
  213. return (V) 0;
  214. } else { // entry already exists, replace its value
  215. // pFree(entry->value); // @NOTE how to cleanup if overwriting an owned pointer?
  216. V oldValue = entry->value;
  217. entry->value = value;
  218. return oldValue;
  219. }
  220. }
  221. TableEntry<V>* lookup(const char* key, u32 keyLength) {
  222. TYPES_H_FTAG;
  223. TableEntry<V>* entry = this->entries[hash(key, keyLength, lanes)];
  224. for (; entry != null; entry = entry->next) {
  225. if (String::memeq((unsigned char*)key, keyLength, (unsigned char*)entry->key, entry->keyLength)) {
  226. return entry;
  227. }
  228. }
  229. return null;
  230. }
  231. V lookupWithDefault(const char* key, u32 keyLength, V defaultValue) {
  232. TYPES_H_FTAG;
  233. auto entry = this->lookup(key, keyLength);
  234. if (entry == null) return defaultValue;
  235. return entry->value;
  236. }
  237. // do not set |freeValues| to true unless the template parameter 'T' is a pointer,
  238. // and the table is responsible for freeing the memory.
  239. void clear(bool freeValues = false) {
  240. TYPES_H_FTAG;
  241. for (u32 i = 0; i < this->lanes; i++) {
  242. TableEntry<V>** lane = &this->entries[i];
  243. TableEntry<V>* entry = *lane;
  244. while (entry != null) {
  245. auto next = entry->next;
  246. pFree(entry->key);
  247. if (freeValues) {
  248. // @HACK - it's only relevant to free the value if it's an owned pointer
  249. // (the table is effectively 'responsible' for that memory)
  250. // but you may have 'V' be a non-pointer value entirely, causing this cast to
  251. // be nonsensical/a bug in other cases.
  252. //
  253. // make sure you know what you're doing when you set |freeValues| to |true|.
  254. //
  255. // there's probably a 'better' way to do this using C++ template voodoo,
  256. // but I have no interest in digging myself a deeper grave there.
  257. #ifdef __clang__
  258. #pragma clang diagnostic push
  259. #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"
  260. #endif
  261. pFree((void*) entry->value);
  262. #ifdef __clang__
  263. #pragma clang diagnostic pop
  264. #endif
  265. }
  266. pFree(entry);
  267. entry = next;
  268. }
  269. *lane = null;
  270. }
  271. this->length = 0;
  272. }
  273. void traverse(const std::function <void (TableEntry<V>*)>& entryCallback) {
  274. TYPES_H_FTAG;
  275. for (u32 i = 0; i < this->lanes; i++) {
  276. TableEntry<V>* entry = this->entries[i];
  277. while (entry != null) {
  278. entryCallback(entry);
  279. entry = entry->next;
  280. }
  281. }
  282. }
  283. };
  284. template <typename T>
  285. static void serialize(String* str, Table<T> table) {
  286. TYPES_H_FTAG;
  287. serialize(str, table.lanes);
  288. serialize(str, table.length);
  289. for (u32 i = 0; i < table.lanes; i++) {
  290. TableEntry<T>* entry = table.entries[i];
  291. while (entry) {
  292. serialize(str, entry);
  293. entry = entry->next;
  294. }
  295. }
  296. }
  297. template <typename T>
  298. static void serialize(String* str, Table<T>* table) {
  299. TYPES_H_FTAG;
  300. SERIALIZE_HANDLE_NULL(str, table);
  301. serialize(str, table->lanes);
  302. serialize(str, table->length);
  303. for (u32 i = 0; i < table->lanes; i++) {
  304. TableEntry<T>* entry = table->entries[i];
  305. while (entry) {
  306. serialize(str, entry);
  307. entry = entry->next;
  308. }
  309. }
  310. }
  311. template <typename T>
  312. static void deserialize(char** buffer, Table<T>* table) {
  313. TYPES_H_FTAG;
  314. deserialize(buffer, &table->lanes);
  315. u32 length;
  316. deserialize(buffer, &length);
  317. for (u32 i = 0; i < length; i++) {
  318. TableEntry<T> entry;
  319. deserialize(buffer, &entry.key);
  320. deserialize(buffer, &entry.keyLength);
  321. deserialize(buffer, &entry.value);
  322. table->insert(entry.key, entry.keyLength, entry.value);
  323. pFree(entry.key);
  324. }
  325. table->length = length;
  326. }
  327. template <typename T>
  328. static void deserialize(char** buffer, Table<T>** table) {
  329. TYPES_H_FTAG;
  330. DESERIALIZE_HANDLE_NULL(buffer, table);
  331. u32 lanes;
  332. deserialize(buffer, &lanes);
  333. Table<T>* _table = new Table<T>(lanes);
  334. u32 length;
  335. deserialize(buffer, &length);
  336. for (u32 i = 0; i < length; i++) {
  337. TableEntry<T> entry;
  338. deserialize(buffer, &entry.key);
  339. deserialize(buffer, &entry.keyLength);
  340. deserialize(buffer, &entry.value);
  341. _table->insert(entry.key, entry.keyLength, entry.value);
  342. pFree(entry.key);
  343. }
  344. _table->length = length;
  345. *table = _table;
  346. }
  347. //================================================================================
  348. // Fixed-key size table.
  349. //
  350. // Sometimes, you want a hash table and you know for a fact how big the keys will
  351. // be at maximum.
  352. //
  353. // This commonly happens when the keys actually aren't strings, but are integers,
  354. // or other packed values.
  355. //
  356. // You can use this instead in that case, and avoid keeping certain memory and
  357. // some work the more generic table has to deal with.
  358. //
  359. // You'll enjoy some speedup from MMX/SSE/AVX if they are supported and you use
  360. // a key size of 16, 32, or 64.
  361. //
  362. //================================================================================
  363. //#include <mmintrin.h>
  364. template <size_t KEY_SIZE, typename std::enable_if<KEY_SIZE == 64>::type* = nullptr>
  365. static inline bool fixedKeySizeMemEq(u8* m1, u8* m2) {
  366. TYPES_H_FTAG;
  367. // AVX512:
  368. //__mmask32 result = _mm512_cmpeq_epi16_mask (*((__m512i*)m1), *((__m512i*)m2));
  369. //sse4.2:
  370. //int result = 0;
  371. //for (u32 i = 0; i < 4; i++) {
  372. // __m128i s1 = *((__m128i*)(m1+(i*16)));
  373. // __m128i s2 = *((__m128i*)(m2+(i*16)));
  374. // result = _mm_cmpistro(s1, s2, _SIDD_UBYTE_OPS);
  375. //}
  376. // MMX: (this one is barely nanoseconds (~1-10ns) faster than String::memeq)
  377. //for (u32 i = 0; i < 4; i++) {
  378. // u32 ii = i*16;
  379. // __m64 s1 = *((__m64*)(m1+ii));
  380. // __m64 s2 = *((__m64*)(m2+ii));
  381. // __m64 result = _mm_cmpeq_pi32(s1, s2);
  382. // if (((u64)result) != ~0ULL) {
  383. // return false;
  384. // }
  385. //}
  386. //return true;
  387. return String::memeq(m1, m2, KEY_SIZE);
  388. }
  389. template <size_t KEY_SIZE, typename std::enable_if<KEY_SIZE == 32>::type* = nullptr>
  390. static inline bool fixedKeySizeMemEq(u8* m1, u8* m2) {
  391. TYPES_H_FTAG;
  392. //sse4.2:
  393. //int result = 0;
  394. //for (u32 i = 0; i < 4; i++) {
  395. // __m128i s1 = *((__m128i*)(m1+(i*16)));
  396. // __m128i s2 = *((__m128i*)(m2+(i*16)));
  397. // result = _mm_cmpistro(s1, s2, _SIDD_UBYTE_OPS);
  398. //}
  399. // MMX: (this one is barely nanoseconds (~1-10ns) faster than String::memeq)
  400. //for (u32 i = 0; i < 2; i++) {
  401. // u32 ii = i*16;
  402. // __m64 s1 = *((__m64*)(m1+ii));
  403. // __m64 s2 = *((__m64*)(m2+ii));
  404. // __m64 result = _mm_cmpeq_pi32(s1, s2);
  405. // if (((u64)result) != ~0ULL) {
  406. // return false;
  407. // }
  408. //}
  409. //return true;
  410. return String::memeq(m1, m2, KEY_SIZE);
  411. }
  412. template <size_t KEY_SIZE, typename std::enable_if<KEY_SIZE == 16>::type* = nullptr>
  413. static inline bool fixedKeySizeMemEq(u8* m1, u8* m2) {
  414. TYPES_H_FTAG;
  415. // MMX: (this one is barely nanoseconds (~1-10ns) faster than String::memeq)
  416. //__m64 result = _mm_cmpeq_pi32(*((__m64*)m1), *((__m64*)m2));
  417. //return ((u64)result) == ~0ULL;
  418. return String::memeq(m1, m2, KEY_SIZE);
  419. }
  420. template <size_t KEY_SIZE, typename std::enable_if<KEY_SIZE != 64 && KEY_SIZE != 32 && KEY_SIZE != 16>::type* = nullptr>
  421. static inline bool fixedKeySizeMemEq(u8* m1, u8* m2) {
  422. TYPES_H_FTAG;
  423. return String::memeq(m1, m2, KEY_SIZE);
  424. }
  425. template <size_t KEY_SIZE, typename V>
  426. struct FixedKeySizeTableEntry {
  427. FixedKeySizeTableEntry<KEY_SIZE, V>* next;
  428. const char key[KEY_SIZE];
  429. V value;
  430. };
  431. template <size_t KEY_SIZE, typename V>
  432. struct FixedKeySizeTable {
  433. u32 lanes;
  434. u32 length;
  435. FixedKeySizeTableEntry<KEY_SIZE, V>** entries;
  436. FixedKeySizeTable<KEY_SIZE, V>(u32 _lanes = 16) {
  437. TYPES_H_FTAG;
  438. this->lanes = _lanes;
  439. this->length = 0;
  440. this->entries = (FixedKeySizeTableEntry<KEY_SIZE, V>**) pCalloc(sizeof(FixedKeySizeTableEntry<KEY_SIZE, V>*), this->lanes);
  441. }
  442. void* operator new(size_t size) {
  443. TYPES_H_FTAG;
  444. return (FixedKeySizeTable<KEY_SIZE, V>*) pMalloc(sizeof(FixedKeySizeTable<KEY_SIZE, V>));
  445. }
  446. V insert(const char* key, V value) {
  447. TYPES_H_FTAG;
  448. FixedKeySizeTableEntry<KEY_SIZE, V>* entry = this->lookup(key);
  449. if (!entry) { // no entry with that key exists
  450. entry = (FixedKeySizeTableEntry<KEY_SIZE, V>*) pCalloc(sizeof(FixedKeySizeTableEntry<KEY_SIZE, V>), 1);
  451. String::write((char*)entry->key, key, KEY_SIZE);
  452. entry->value = value;
  453. u32 hashValue = hash(key, KEY_SIZE, lanes);
  454. entry->next = entries[hashValue];
  455. entries[hashValue] = entry;
  456. this->length++;
  457. return (V) 0;
  458. } else { // entry already exists, replace its value
  459. // pFree(entry->value); // @NOTE how to cleanup if overwriting an owned pointer?
  460. V oldValue = entry->value;
  461. entry->value = value;
  462. return oldValue;
  463. }
  464. }
  465. FixedKeySizeTableEntry<KEY_SIZE, V>* lookup(const char* key) {
  466. TYPES_H_FTAG;
  467. FixedKeySizeTableEntry<KEY_SIZE, V>* entry = this->entries[hash(key, KEY_SIZE, lanes)];
  468. for (; entry != null; entry = entry->next) {
  469. if (fixedKeySizeMemEq<KEY_SIZE>((unsigned char*)key, (unsigned char*)entry->key)) {
  470. return entry;
  471. }
  472. }
  473. return null;
  474. }
  475. V lookupWithDefault(const char* key, V defaultValue) {
  476. TYPES_H_FTAG;
  477. auto entry = this->lookup(key);
  478. if (entry == null) return defaultValue;
  479. return entry->value;
  480. }
  481. // do not set |freeValues| to true unless the template parameter 'T' is a pointer,
  482. // and the table is responsible for freeing the memory.
  483. void clear(bool freeValues = false) {
  484. TYPES_H_FTAG;
  485. for (u32 i = 0; i < this->lanes; i++) {
  486. FixedKeySizeTableEntry<KEY_SIZE, V>** lane = &this->entries[i];
  487. FixedKeySizeTableEntry<KEY_SIZE, V>* entry = *lane;
  488. while (entry != null) {
  489. auto next = entry->next;
  490. if (freeValues) {
  491. // @HACK - it's only relevant to free the value if it's an owned pointer
  492. // (the table is effectively 'responsible' for that memory)
  493. // but you may have 'V' be a non-pointer value entirely, causing this cast to
  494. // be nonsensical/a bug in other cases.
  495. //
  496. // make sure you know what you're doing when you set |freeValues| to |true|.
  497. //
  498. // there's probably a 'better' way to do this using C++ template voodoo,
  499. // but I have no interest in digging myself a deeper grave there.
  500. #ifdef __clang__
  501. #pragma clang diagnostic push
  502. #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"
  503. #endif
  504. pFree((void*) entry->value);
  505. #ifdef __clang__
  506. #pragma clang diagnostic pop
  507. #endif
  508. }
  509. pFree(entry);
  510. entry = next;
  511. }
  512. *lane = null;
  513. }
  514. this->length = 0;
  515. }
  516. void traverse(const std::function <void (FixedKeySizeTableEntry<KEY_SIZE, V>*)>& entryCallback) {
  517. TYPES_H_FTAG;
  518. for (u32 i = 0; i < this->lanes; i++) {
  519. FixedKeySizeTableEntry<KEY_SIZE, V>* entry = this->entries[i];
  520. while (entry != null) {
  521. entryCallback(entry);
  522. entry = entry->next;
  523. }
  524. }
  525. }
  526. };
  527. //================================================================================
  528. // a better explaination of cache tables than I could possibly do in a comment:
  529. // https://fgiesen.wordpress.com/2019/02/11/cache-tables/
  530. struct CacheTableEntry {
  531. char* key;
  532. u32 keyLength;
  533. void* value;
  534. };
  535. struct CacheTable {
  536. u32 n, p;
  537. CacheTableEntry* entries; // n and p are the dimensions of the array. n is first.
  538. CacheTable(u32 _n = 8, u32 _p = 8) {
  539. TYPES_H_FTAG;
  540. this->n = _n;
  541. this->p = _p;
  542. this->entries = (CacheTableEntry*) pCalloc(this->n*this->p, sizeof(CacheTableEntry));
  543. }
  544. void* insert(const char* key, u32 keyLength, void* value) {
  545. TYPES_H_FTAG;
  546. CacheTableEntry* row = this->entries + hash(key, keyLength, this->n) * this->n;
  547. // We're going to insert in 'row'. We need some policy to decide which column to evict.
  548. // The (stupid) choice for now, is to just start at column 0, and increment every time we insert,
  549. // regardless of which row we chose.
  550. static u32 seed = 0;
  551. // increment, then modulo the number of columns.
  552. seed = ((u64) (seed+1) * (u64) this->p) >> 32;
  553. CacheTableEntry* entry = row + seed;
  554. if (entry->key != null) {
  555. // the entry was already populated. we have to free the memory for the key
  556. // (because we always copy keys on insertion)
  557. // as well as return the address of the old value we overwrite, so the caller can free it
  558. // if necessary.
  559. pFree(entry->key);
  560. entry->key = String::cpy(key, keyLength);
  561. entry->keyLength = keyLength;
  562. void* oldValue = entry->value;
  563. entry->value = value;
  564. return oldValue;
  565. } else {
  566. entry->key = String::cpy(key, keyLength);
  567. entry->keyLength = keyLength;
  568. entry->value = value;
  569. return null;
  570. }
  571. }
  572. CacheTableEntry* lookup(const char* key, u32 keyLength) {
  573. TYPES_H_FTAG;
  574. CacheTableEntry* row = this->entries + hash(key, keyLength, this->n) * this->n;
  575. for (u32 i = 0; i < this->p; i++) {
  576. CacheTableEntry* entry = row + i;
  577. if (String::memeq((unsigned char*)key, keyLength, (unsigned char*)entry->key, entry->keyLength)) {
  578. return entry;
  579. }
  580. }
  581. return null;
  582. }
  583. void clear(bool freeValues = false) {
  584. TYPES_H_FTAG;
  585. for (u32 i = 0; i < this->n; i++) {
  586. CacheTableEntry* row = this->entries + i * this->n;
  587. for (u32 j = 0; j < this->p; j++) {
  588. CacheTableEntry* entry = row + j;
  589. if (entry->key != null) {
  590. pFree(entry->key);
  591. }
  592. if (freeValues && entry->value != null) {
  593. pFree(entry->value);
  594. }
  595. }
  596. }
  597. }
  598. };
  599. #endif