htable.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <limits.h>
  5. #include <assert.h>
  6. #include "htable.h"
  7. #define BUCKET_EMPTY 1 << 0
  8. #define BUCKET_DELETED 1 << 1
  9. #if defined(DEBUG)
  10. #define ASSERT assert
  11. #else
  12. #define ASSERT(x)
  13. #endif
  14. #define MAX_GROWTH_STEP (1024U * 1024)
  15. #define APPROX_85_PERCENT(x) (((x) * 870) >> 10)
  16. #define APPROX_40_PERCENT(x) (((x) * 409) >> 10)
  17. #define MIN_HT_SIZE 1 << 3
  18. typedef struct {
  19. LSUP_TripleKey key; // TODO Make configurable but
  20. // statically allocated via macros
  21. void * val;
  22. uint64_t hash;
  23. uint16_t psl;
  24. } bucket_t;
  25. typedef struct htable_t {
  26. htsize_t size;
  27. htsize_t nitems;
  28. unsigned flags;
  29. uint64_t divinfo;
  30. bucket_t * buckets;
  31. uint64_t seed;
  32. key_hash_fn_t key_hash_fn;
  33. key_eq_fn_t key_eq_fn;
  34. ksize_t ksize;
  35. vsize_t vsize;
  36. void * del_marker; // Used to fill deleted buckets.
  37. } HTable;
  38. /* * * GENERIC UTILITIES * * */
  39. static inline bool is_empty_bucket(const HTable *ht, const bucket_t *bucket)
  40. { return memcmp(bucket->key, ht->del_marker, ht->ksize) == 0; }
  41. /*
  42. * Find first bit.
  43. */
  44. static inline int fls(int x)
  45. { return x ? (sizeof(int) * CHAR_BIT) - __builtin_clz(x) : 0; }
  46. /*
  47. * Fast 32bit division and remainder.
  48. *
  49. * Reference:
  50. *
  51. * Torbjörn Granlund and Peter L. Montgomery, "Division by Invariant
  52. * Integers Using Multiplication", ACM SIGPLAN Notices, Issue 6, Vol 29,
  53. * http://gmplib.org/~tege/divcnst-pldi94.pdf, 61-72, June 1994.
  54. *
  55. * The following example computes q = a / b and r = a % b:
  56. *
  57. * uint64_t divinfo = fast_div32_init(b);
  58. * q = fast_div32(a, b, divinfo);
  59. * r = fast_rem32(a, b, divinfo);
  60. */
  61. static inline uint64_t
  62. fast_div32_init(uint32_t div)
  63. {
  64. uint64_t mt;
  65. uint8_t s1, s2;
  66. int l;
  67. l = fls(div - 1);
  68. mt = (uint64_t)(0x100000000ULL * ((1ULL << l) - div));
  69. s1 = (l > 1) ? 1U : (uint8_t)l;
  70. s2 = (l == 0) ? 0 : (uint8_t)(l - 1);
  71. return (uint64_t)(mt / div + 1) << 32 | (uint32_t)s1 << 8 | s2;
  72. }
  73. static inline uint32_t
  74. fast_div32(uint32_t v, uint32_t div, uint64_t divinfo)
  75. {
  76. const uint32_t m = divinfo >> 32;
  77. const unsigned s1 = (divinfo & 0x0000ff00) >> 8;
  78. const unsigned s2 = (divinfo & 0x000000ff);
  79. const uint32_t t = (uint32_t)(((uint64_t)v * m) >> 32);
  80. (void)div; // unused
  81. return (t + ((v - t) >> s1)) >> s2;
  82. }
  83. static inline uint32_t
  84. fast_rem32(uint32_t v, uint32_t div, uint64_t divinfo)
  85. { return v - div * fast_div32(v, div, divinfo); }
  86. static int __attribute__((__unused__))
  87. //static int
  88. validate_psl_p(const HTable *ht, const bucket_t *bucket, unsigned i)
  89. {
  90. unsigned base_i = fast_rem32(bucket->hash, ht->size, ht->divinfo);
  91. unsigned diff = (base_i > i) ? ht->size - base_i + i : i - base_i;
  92. return is_empty_bucket(ht, bucket) || diff == bucket->psl;
  93. }
  94. /* * * PUBLIC API * * */
  95. HTable *LSUP_htable_new(
  96. htsize_t size, ksize_t ksize, vsize_t vsize,
  97. key_hash_fn_t key_hash_fn, key_eq_fn_t key_eq_fn, unsigned flags)
  98. {
  99. HTable *ht;
  100. CRITICAL(ht = calloc(1, sizeof(HTable)));
  101. ht->ksize = ksize;
  102. ht->vsize = vsize;
  103. ht->key_hash_fn = key_hash_fn;
  104. ht->key_eq_fn = key_eq_fn;
  105. ht->flags = flags;
  106. ht->size = 0;
  107. CRITICAL(ht->del_marker = calloc(1, ksize));
  108. LSUP_htable_resize(ht, size);
  109. return ht;
  110. }
  111. /**
  112. * Resize a table.
  113. */
  114. int LSUP_htable_resize(HTable *ht, htsize_t newsize)
  115. {
  116. TRACE("Resizing htable to %lu.", (size_t)newsize);
  117. bucket_t *oldbuckets = ht->buckets;
  118. const htsize_t oldsize = ht->size;
  119. // Clip size to min & max limits.
  120. if (newsize < MIN_HT_SIZE) newsize = MIN_HT_SIZE;
  121. if (newsize > HTSIZE_MAX) newsize = HTSIZE_MAX;
  122. CRITICAL(ht->buckets = calloc(newsize, sizeof(bucket_t)));
  123. ht->size = newsize;
  124. ht->nitems = 0;
  125. ht->divinfo = fast_div32_init(newsize);
  126. ht->seed ^= random() | (random() << 32);
  127. for (unsigned i = 0; i < oldsize; i++) {
  128. const bucket_t *bucket = &oldbuckets[i];
  129. /* Skip the empty buckets. */
  130. if (!is_empty_bucket(ht, bucket))
  131. LSUP_htable_insert(ht, bucket->key, bucket->val);
  132. }
  133. if (oldbuckets != NULL) free(oldbuckets);
  134. return LSUP_OK;
  135. }
  136. htsize_t LSUP_htable_capacity(LSUP_HTable *ht)
  137. { return ht->size; }
  138. htsize_t LSUP_htable_size(LSUP_HTable *ht)
  139. { return ht->nitems; }
  140. /*
  141. * Insert without resizing (assuming resizing is already done).
  142. */
  143. int LSUP_htable_insert(HTable *ht, const void *key, void *val)
  144. {
  145. bucket_t *bucket, entry;
  146. ASSERT(key != NULL);
  147. /*
  148. * Setup the bucket entry.
  149. */
  150. memcpy(entry.key, key, ht->ksize);
  151. //memcpy(entry.val, val, ht->vsize);
  152. entry.val = val;
  153. entry.hash = ht->key_hash_fn(entry.key, ht->ksize, ht->seed);
  154. entry.psl = 0;
  155. /*
  156. * From the paper: "when inserting, if a record probes a location
  157. * that is already occupied, the record that has traveled longer
  158. * in its probe sequence keeps the location, and the other one
  159. * continues on its probe sequence" (page 12).
  160. *
  161. * Basically: if the probe sequence length (PSL) of the element
  162. * being inserted is greater than PSL of the element in the bucket,
  163. * then swap them and continue.
  164. */
  165. htsize_t i = fast_rem32(entry.hash, ht->size, ht->divinfo);
  166. for(;;) {
  167. bucket = ht->buckets + i;
  168. if(is_empty_bucket(ht, ht->buckets + i)) break;
  169. ASSERT(validate_psl_p(ht, bucket, i));
  170. // There is a key in the bucket.
  171. TRACE("Entry key: {%lu, %lu, %lu}; bucket key: {%lu, %lu, %lu}", entry.key[0], entry.key[1], entry.key[2], bucket->key[0], bucket->key[1], bucket->key[2]);
  172. if (ht->key_eq_fn(bucket->key, entry.key, ht->ksize)) {
  173. // Duplicate key: do nothing.
  174. TRACE(STR, "Duplicate key.");
  175. return LSUP_NOACTION;
  176. }
  177. /*
  178. * We found a "rich" bucket. Capture its location.
  179. */
  180. if (entry.psl > bucket->psl) {
  181. //TRACE("Entry PSL: %d; Bucket PSL: %d", entry.psl, bucket->psl);
  182. bucket_t tmp;
  183. TRACE(STR, "SWAP");
  184. /*
  185. * Place our key-value pair by swapping the "rich"
  186. * bucket with our entry. Copy the structures.
  187. */
  188. tmp = entry;
  189. entry = *bucket;
  190. *bucket = tmp;
  191. }
  192. entry.psl++;
  193. /* Continue to the next bucket. */
  194. ASSERT(validate_psl_p(ht, bucket, i));
  195. i = fast_rem32(i + 1, ht->size, ht->divinfo);
  196. }
  197. /*
  198. * Found a free bucket: insert the entry.
  199. */
  200. TRACE("Inserting {%lu, %lu, %lu} in bucket #%d", entry.key[0], entry.key[1], entry.key[2], i);
  201. //*bucket = entry; // copy
  202. memcpy(bucket, &entry, sizeof(bucket_t)); // copy
  203. ht->nitems++;
  204. ASSERT(validate_psl_p(ht, bucket, i));
  205. return LSUP_OK;
  206. }
  207. /*
  208. * rhashmap_put: insert a value given the key.
  209. *
  210. * => If the key is already present, return its associated value.
  211. * => Otherwise, on successful insert, return the given value.
  212. */
  213. int LSUP_htable_put(HTable *ht, const void *key, void *val)
  214. {
  215. const size_t threshold = APPROX_85_PERCENT(ht->size);
  216. /*
  217. * If the load factor is more than the threshold, then resize.
  218. */
  219. if (UNLIKELY(ht->nitems > threshold)) {
  220. /*
  221. * Grow the hash table by doubling its size, but with
  222. * a limit of MAX_GROWTH_STEP.
  223. */
  224. const size_t grow_limit = ht->size + MAX_GROWTH_STEP;
  225. const size_t newsize = min(ht->size << 1, grow_limit);
  226. LSUP_htable_resize(ht, newsize);
  227. }
  228. return LSUP_htable_insert(ht, key, val);
  229. }
  230. int LSUP_htable_get(const HTable *ht, const void *key, void **valp)
  231. {
  232. const uint64_t hash = ht->key_hash_fn(key, ht->ksize, ht->seed);
  233. htsize_t n = 0, i = fast_rem32(hash, ht->size, ht->divinfo);
  234. if (key == NULL) return LSUP_VALUE_ERR;
  235. /*
  236. * Lookup is a linear probe.
  237. */
  238. for(;;) {
  239. bucket_t *bucket = ht->buckets + i;
  240. ASSERT(validate_psl_p(ht, bucket, i));
  241. if (ht->key_eq_fn(bucket->key, key, ht->ksize)) {
  242. // Key found within max probe length.
  243. if (valp != NULL)
  244. *valp = bucket->val;
  245. return LSUP_OK;
  246. }
  247. /*
  248. * Stop probing if we hit an empty bucket; also, if we hit a
  249. * bucket with PSL lower than the distance from the base location,
  250. * then it means that we found the "rich" bucket which should
  251. * have been captured, if the key was inserted -- see the central
  252. * point of the algorithm in the insertion function.
  253. */
  254. if (is_empty_bucket(ht, bucket) || n > bucket->psl) {
  255. valp = NULL;
  256. return LSUP_NORESULT;
  257. }
  258. n++;
  259. /* Continue to the next bucket. */
  260. i = fast_rem32(i + 1, ht->size, ht->divinfo);
  261. }
  262. }
  263. int LSUP_htable_del(HTable *ht, const void *key)
  264. {
  265. const size_t threshold = APPROX_40_PERCENT(ht->size);
  266. const uint32_t hash = ht->key_hash_fn(key, ht->ksize, ht->seed);
  267. unsigned n = 0, i = fast_rem32(hash, ht->size, ht->divinfo);
  268. bucket_t *bucket;
  269. ASSERT(key != NULL);
  270. for(;;) {
  271. /*
  272. * The same probing logic as in the lookup function.
  273. */
  274. bucket_t *bucket = ht->buckets + i;
  275. if (is_empty_bucket(ht, bucket) || n > bucket->psl)
  276. return LSUP_NOACTION;
  277. ASSERT(validate_psl_p(ht, bucket, i));
  278. if (!ht->key_eq_fn(bucket->key, key, ht->ksize)) {
  279. /* Continue to the next bucket. */
  280. i = fast_rem32(i + 1, ht->size, ht->divinfo);
  281. n++;
  282. }
  283. }
  284. ht->nitems--;
  285. /*
  286. * The probe sequence must be preserved in the deletion case.
  287. * Use the backwards-shifting method to maintain low variance.
  288. */
  289. while(1) {
  290. bucket_t *nbucket;
  291. memcpy(bucket->key, ht->del_marker, ht->ksize);
  292. i = fast_rem32(i + 1, ht->size, ht->divinfo);
  293. nbucket = ht->buckets + i;
  294. ASSERT(validate_psl_p(ht, nbucket, i));
  295. /*
  296. * Stop if we reach an empty bucket or hit a key which
  297. * is in its base (original) location.
  298. */
  299. if (is_empty_bucket(ht, nbucket) || nbucket->psl == 0)
  300. break;
  301. nbucket->psl--;
  302. *bucket = *nbucket;
  303. bucket = nbucket;
  304. }
  305. /*
  306. * If the load factor is less than threshold, then shrink by
  307. * halving the size, but not less than 1.
  308. */
  309. if (ht->nitems < threshold) {
  310. size_t newsize = max(ht->size >> 1, 1);
  311. (void)LSUP_htable_resize(ht, newsize);
  312. }
  313. return LSUP_OK;
  314. }
  315. extern int LSUP_htable_iter(
  316. LSUP_HTable *ht, htsize_t *cur, void **keyp, void **valp)
  317. {
  318. while (*cur < ht->size) {
  319. bucket_t *bucket = ht->buckets + *cur;
  320. (*cur)++;
  321. if (is_empty_bucket(ht, bucket)) {
  322. TRACE("Empty bucket: %d. Skipping.", (*cur) - 1);
  323. continue;
  324. }
  325. // Copy key, and if relevant, value.
  326. *keyp = bucket->key;
  327. if (valp != NULL && ht->vsize > 0) *valp = bucket->val;
  328. return LSUP_OK;
  329. }
  330. return LSUP_END;
  331. }
  332. void LSUP_htable_done(HTable *ht)
  333. {
  334. if(LIKELY(ht->buckets != NULL)) free(ht->buckets);
  335. free(ht->del_marker);
  336. }
  337. void LSUP_htable_free(HTable *ht)
  338. {
  339. if(LIKELY(ht != NULL)) {
  340. LSUP_htable_done(ht);
  341. free(ht);
  342. }
  343. }