关键词搜索

源码搜索 ×
×

漫话Redis源码之六十五

发布2022-01-23浏览573次

详情内容

这里主要是哈希相关的函数,也是基础的内容,任何相关的组件代码必然涉及。

  1. static uint8_t dict_hash_function_seed[16];
  2. void dictSetHashFunctionSeed(uint8_t *seed) {
  3. memcpy(dict_hash_function_seed,seed,sizeof(dict_hash_function_seed));
  4. }
  5. uint8_t *dictGetHashFunctionSeed(void) {
  6. return dict_hash_function_seed;
  7. }
  8. /* The default hashing function uses SipHash implementation
  9. * in siphash.c. */
  10. uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k);
  11. uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k);
  12. uint64_t dictGenHashFunction(const void *key, int len) {
  13. return siphash(key,len,dict_hash_function_seed);
  14. }
  15. uint64_t dictGenCaseHashFunction(const unsigned char *buf, int len) {
  16. return siphash_nocase(buf,len,dict_hash_function_seed);
  17. }
  18. /* ----------------------------- API implementation ------------------------- */
  19. /* Reset a hash table already initialized with ht_init().
  20. * NOTE: This function should only be called by ht_destroy(). */
  21. static void _dictReset(dictht *ht)
  22. {
  23. ht->table = NULL;
  24. ht->size = 0;
  25. ht->sizemask = 0;
  26. ht->used = 0;
  27. }
  28. /* Create a new hash table */
  29. dict *dictCreate(dictType *type,
  30. void *privDataPtr)
  31. {
  32. dict *d = zmalloc(sizeof(*d));
  33. _dictInit(d,type,privDataPtr);
  34. return d;
  35. }
  36. /* Initialize the hash table */
  37. int _dictInit(dict *d, dictType *type,
  38. void *privDataPtr)
  39. {
  40. _dictReset(&d->ht[0]);
  41. _dictReset(&d->ht[1]);
  42. d->type = type;
  43. d->privdata = privDataPtr;
  44. d->rehashidx = -1;
  45. d->pauserehash = 0;
  46. return DICT_OK;
  47. }
  48. /* Resize the table to the minimal size that contains all the elements,
  49. * but with the invariant of a USED/BUCKETS ratio near to <= 1 */
  50. int dictResize(dict *d)
  51. {
  52. unsigned long minimal;
  53. if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
  54. minimal = d->ht[0].used;
  55. if (minimal < DICT_HT_INITIAL_SIZE)
  56. minimal = DICT_HT_INITIAL_SIZE;
  57. return dictExpand(d, minimal);
  58. }
  59. /* Expand or create the hash table,
  60. * when malloc_failed is non-NULL, it'll avoid panic if malloc fails (in which case it'll be set to 1).
  61. * Returns DICT_OK if expand was performed, and DICT_ERR if skipped. */
  62. int _dictExpand(dict *d, unsigned long size, int* malloc_failed)
  63. {
  64. if (malloc_failed) *malloc_failed = 0;
  65. /* the size is invalid if it is smaller than the number of
  66. * elements already inside the hash table */
  67. if (dictIsRehashing(d) || d->ht[0].used > size)
  68. return DICT_ERR;
  69. dictht n; /* the new hash table */
  70. unsigned long realsize = _dictNextPower(size);
  71. /* Detect overflows */
  72. if (realsize < size || realsize * sizeof(dictEntry*) < realsize)
  73. return DICT_ERR;
  74. /* Rehashing to the same table size is not useful. */
  75. if (realsize == d->ht[0].size) return DICT_ERR;
  76. /* Allocate the new hash table and initialize all pointers to NULL */
  77. n.size = realsize;
  78. n.sizemask = realsize-1;
  79. if (malloc_failed) {
  80. n.table = ztrycalloc(realsize*sizeof(dictEntry*));
  81. *malloc_failed = n.table == NULL;
  82. if (*malloc_failed)
  83. return DICT_ERR;
  84. } else
  85. n.table = zcalloc(realsize*sizeof(dictEntry*));
  86. n.used = 0;
  87. /* Is this the first initialization? If so it's not really a rehashing
  88. * we just set the first hash table so that it can accept keys. */
  89. if (d->ht[0].table == NULL) {
  90. d->ht[0] = n;
  91. return DICT_OK;
  92. }
  93. /* Prepare a second hash table for incremental rehashing */
  94. d->ht[1] = n;
  95. d->rehashidx = 0;
  96. return DICT_OK;
  97. }
  98. /* return DICT_ERR if expand was not performed */
  99. int dictExpand(dict *d, unsigned long size) {
  100. return _dictExpand(d, size, NULL);
  101. }
  102. /* return DICT_ERR if expand failed due to memory allocation failure */
  103. int dictTryExpand(dict *d, unsigned long size) {
  104. int malloc_failed;
  105. _dictExpand(d, size, &malloc_failed);
  106. return malloc_failed? DICT_ERR : DICT_OK;
  107. }
  108. /* Performs N steps of incremental rehashing. Returns 1 if there are still
  109. * keys to move from the old to the new hash table, otherwise 0 is returned.
  110. *
  111. * Note that a rehashing step consists in moving a bucket (that may have more
  112. * than one key as we use chaining) from the old to the new hash table, however
  113. * since part of the hash table may be composed of empty spaces, it is not
  114. * guaranteed that this function will rehash even a single bucket, since it
  115. * will visit at max N*10 empty buckets in total, otherwise the amount of
  116. * work it does would be unbound and the function may block for a long time. */
  117. int dictRehash(dict *d, int n) {
  118. int empty_visits = n*10; /* Max number of empty buckets to visit. */
  119. if (!dictIsRehashing(d)) return 0;
  120. while(n-- && d->ht[0].used != 0) {
  121. dictEntry *de, *nextde;
  122. /* Note that rehashidx can't overflow as we are sure there are more
  123. * elements because ht[0].used != 0 */
  124. assert(d->ht[0].size > (unsigned long)d->rehashidx);
  125. while(d->ht[0].table[d->rehashidx] == NULL) {
  126. d->rehashidx++;
  127. if (--empty_visits == 0) return 1;
  128. }
  129. de = d->ht[0].table[d->rehashidx];
  130. /* Move all the keys in this bucket from the old to the new hash HT */
  131. while(de) {
  132. uint64_t h;
  133. nextde = de->next;
  134. /* Get the index in the new hash table */
  135. h = dictHashKey(d, de->key) & d->ht[1].sizemask;
  136. de->next = d->ht[1].table[h];
  137. d->ht[1].table[h] = de;
  138. d->ht[0].used--;
  139. d->ht[1].used++;
  140. de = nextde;
  141. }
  142. d->ht[0].table[d->rehashidx] = NULL;
  143. d->rehashidx++;
  144. }
  145. /* Check if we already rehashed the whole table... */
  146. if (d->ht[0].used == 0) {
  147. zfree(d->ht[0].table);
  148. d->ht[0] = d->ht[1];
  149. _dictReset(&d->ht[1]);
  150. d->rehashidx = -1;
  151. return 0;
  152. }
  153. /* More to rehash... */
  154. return 1;
  155. }
  156. long long timeInMilliseconds(void) {
  157. struct timeval tv;
  158. gettimeofday(&tv,NULL);
  159. return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
  160. }
  161. /* Rehash in ms+"delta" milliseconds. The value of "delta" is larger
  162. * than 0, and is smaller than 1 in most cases. The exact upper bound
  163. * depends on the running time of dictRehash(d,100).*/
  164. int dictRehashMilliseconds(dict *d, int ms) {
  165. if (d->pauserehash > 0) return 0;
  166. long long start = timeInMilliseconds();
  167. int rehashes = 0;
  168. while(dictRehash(d,100)) {
  169. rehashes += 100;
  170. if (timeInMilliseconds()-start > ms) break;
  171. }
  172. return rehashes;
  173. }
  174. /* This function performs just a step of rehashing, and only if hashing has
  175. * not been paused for our hash table. When we have iterators in the
  176. * middle of a rehashing we can't mess with the two hash tables otherwise
  177. * some element can be missed or duplicated.
  178. *
  179. * This function is called by common lookup or update operations in the
  180. * dictionary so that the hash table automatically migrates from H1 to H2
  181. * while it is actively used. */
  182. static void _dictRehashStep(dict *d) {
  183. if (d->pauserehash == 0) dictRehash(d,1);
  184. }
  185. /* Add an element to the target hash table */
  186. int dictAdd(dict *d, void *key, void *val)
  187. {
  188. dictEntry *entry = dictAddRaw(d,key,NULL);
  189. if (!entry) return DICT_ERR;
  190. dictSetVal(d, entry, val);
  191. return DICT_OK;
  192. }
  193. /* Low level add or find:
  194. * This function adds the entry but instead of setting a value returns the
  195. * dictEntry structure to the user, that will make sure to fill the value
  196. * field as they wish.
  197. *
  198. * This function is also directly exposed to the user API to be called
  199. * mainly in order to store non-pointers inside the hash value, example:
  200. *
  201. * entry = dictAddRaw(dict,mykey,NULL);
  202. * if (entry != NULL) dictSetSignedIntegerVal(entry,1000);
  203. *
  204. * Return values:
  205. *
  206. * If key already exists NULL is returned, and "*existing" is populated
  207. * with the existing entry if existing is not NULL.
  208. *
  209. * If key was added, the hash entry is returned to be manipulated by the caller.
  210. */
  211. dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)
  212. {
  213. long index;
  214. dictEntry *entry;
  215. dictht *ht;
  216. if (dictIsRehashing(d)) _dictRehashStep(d);
  217. /* Get the index of the new element, or -1 if
  218. * the element already exists. */
  219. if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)
  220. return NULL;
  221. /* Allocate the memory and store the new entry.
  222. * Insert the element in top, with the assumption that in a database
  223. * system it is more likely that recently added entries are accessed
  224. * more frequently. */
  225. ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
  226. entry = zmalloc(sizeof(*entry));
  227. entry->next = ht->table[index];
  228. ht->table[index] = entry;
  229. ht->used++;
  230. /* Set the hash entry fields. */
  231. dictSetKey(d, entry, key);
  232. return entry;
  233. }
  234. /* Add or Overwrite:
  235. * Add an element, discarding the old value if the key already exists.
  236. * Return 1 if the key was added from scratch, 0 if there was already an
  237. * element with such key and dictReplace() just performed a value update
  238. * operation. */
  239. int dictReplace(dict *d, void *key, void *val)
  240. {
  241. dictEntry *entry, *existing, auxentry;
  242. /* Try to add the element. If the key
  243. * does not exists dictAdd will succeed. */
  244. entry = dictAddRaw(d,key,&existing);
  245. if (entry) {
  246. dictSetVal(d, entry, val);
  247. return 1;
  248. }
  249. /* Set the new value and free the old one. Note that it is important
  250. * to do that in this order, as the value may just be exactly the same
  251. * as the previous one. In this context, think to reference counting,
  252. * you want to increment (set), and then decrement (free), and not the
  253. * reverse. */
  254. auxentry = *existing;
  255. dictSetVal(d, existing, val);
  256. dictFreeVal(d, &auxentry);
  257. return 0;
  258. }
  259. /* Add or Find:
  260. * dictAddOrFind() is simply a version of dictAddRaw() that always
  261. * returns the hash entry of the specified key, even if the key already
  262. * exists and can't be added (in that case the entry of the already
  263. * existing key is returned.)
  264. *
  265. * See dictAddRaw() for more information. */
  266. dictEntry *dictAddOrFind(dict *d, void *key) {
  267. dictEntry *entry, *existing;
  268. entry = dictAddRaw(d,key,&existing);
  269. return entry ? entry : existing;
  270. }
  271. /* Search and remove an element. This is an helper function for
  272. * dictDelete() and dictUnlink(), please check the top comment
  273. * of those functions. */
  274. static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {
  275. uint64_t h, idx;
  276. dictEntry *he, *prevHe;
  277. int table;
  278. if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL;
  279. if (dictIsRehashing(d)) _dictRehashStep(d);
  280. h = dictHashKey(d, key);
  281. for (table = 0; table <= 1; table++) {
  282. idx = h & d->ht[table].sizemask;
  283. he = d->ht[table].table[idx];
  284. prevHe = NULL;
  285. while(he) {
  286. if (key==he->key || dictCompareKeys(d, key, he->key)) {
  287. /* Unlink the element from the list */
  288. if (prevHe)
  289. prevHe->next = he->next;
  290. else
  291. d->ht[table].table[idx] = he->next;
  292. if (!nofree) {
  293. dictFreeKey(d, he);
  294. dictFreeVal(d, he);
  295. zfree(he);
  296. }
  297. d->ht[table].used--;
  298. return he;
  299. }
  300. prevHe = he;
  301. he = he->next;
  302. }
  303. if (!dictIsRehashing(d)) break;
  304. }
  305. return NULL; /* not found */
  306. }
  307. /* Remove an element, returning DICT_OK on success or DICT_ERR if the
  308. * element was not found. */
  309. int dictDelete(dict *ht, const void *key) {
  310. return dictGenericDelete(ht,key,0) ? DICT_OK : DICT_ERR;
  311. }
  312. /* Remove an element from the table, but without actually releasing
  313. * the key, value and dictionary entry. The dictionary entry is returned
  314. * if the element was found (and unlinked from the table), and the user
  315. * should later call `dictFreeUnlinkedEntry()` with it in order to release it.
  316. * Otherwise if the key is not found, NULL is returned.
  317. *
  318. * This function is useful when we want to remove something from the hash
  319. * table but want to use its value before actually deleting the entry.
  320. * Without this function the pattern would require two lookups:
  321. *
  322. * entry = dictFind(...);
  323. * // Do something with entry
  324. * dictDelete(dictionary,entry);
  325. *
  326. * Thanks to this function it is possible to avoid this, and use
  327. * instead:
  328. *
  329. * entry = dictUnlink(dictionary,entry);
  330. * // Do something with entry
  331. * dictFreeUnlinkedEntry(entry); // <- This does not need to lookup again.
  332. */
  333. dictEntry *dictUnlink(dict *ht, const void *key) {
  334. return dictGenericDelete(ht,key,1);
  335. }

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载