simple_hashtable.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #ifndef NETDATA_SIMPLE_HASHTABLE_H
  3. #define NETDATA_SIMPLE_HASHTABLE_H
  4. typedef uint64_t SIMPLE_HASHTABLE_HASH;
  5. #define SIMPLE_HASHTABLE_HASH_SECOND_HASH_SHIFTS 32
  6. /*
  7. * CONFIGURATION
  8. *
  9. * SIMPLE_HASHTABLE_NAME
  10. * The name of the hashtable - all functions and defines will have this name appended
  11. * Example: #define SIMPLE_HASHTABLE_NAME _FACET_KEY
  12. *
  13. * SIMPLE_HASHTABLE_VALUE_TYPE and SIMPLE_HASHTABLE_KEY_TYPE
  14. * The data types of values and keys - optional - setting them will enable strict type checking by the compiler.
  15. * If undefined, they both default to void.
  16. *
  17. * SIMPLE_HASHTABLE_SORT_FUNCTION
  18. * A function name that accepts 2x values and compares them for sorting (returning -1, 0, 1).
  19. * When set, the hashtable will maintain an always sorted array of the values in the hashtable.
  20. * Do not use this for non-static hashtables. So, if your data is changing all the time, this can make the
  21. * hashtable quite slower (it memmove()s an array of pointers to keep it sorted, on every single change).
  22. *
  23. * SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION and SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION
  24. * The hashtable can either compare just hashes (the default), or hashes and keys (when these are set).
  25. * Both need to be set for this feature to be enabled.
  26. *
  27. * - SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION
  28. * The name of a function accepting SIMPLE_HASHTABLE_VALUE_TYPE pointer.
  29. * It should return a pointer to SIMPLE_HASHTABLE_KEY_TYPE.
  30. * This function is called prior to SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION to extract the key from a value.
  31. * It is also called during hashtable resize, to rehash all values in the hashtable.
  32. *
  33. * - SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION
  34. * The name of a function accepting 2x SIMPLE_HASHTABLE_KEY_TYPE pointers.
  35. * It should return true when the keys match.
  36. * This function is only called when the hashes match, to verify that the keys also match.
  37. *
  38. * SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION
  39. * If defined, 3x functions will be injected for easily working with the hashtable.
  40. *
  41. */
  42. #ifndef SIMPLE_HASHTABLE_NAME
  43. #define SIMPLE_HASHTABLE_NAME
  44. #endif
  45. #ifndef SIMPLE_HASHTABLE_VALUE_TYPE
  46. #define SIMPLE_HASHTABLE_VALUE_TYPE void
  47. #endif
  48. #ifndef SIMPLE_HASHTABLE_KEY_TYPE
  49. #define SIMPLE_HASHTABLE_KEY_TYPE void
  50. #endif
  51. #ifndef SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION
  52. #undef SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION
  53. #endif
  54. #if defined(SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION)
  55. static inline SIMPLE_HASHTABLE_KEY_TYPE *SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION(SIMPLE_HASHTABLE_VALUE_TYPE *);
  56. #endif
  57. #if defined(SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION)
  58. static inline bool SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION(SIMPLE_HASHTABLE_KEY_TYPE *, SIMPLE_HASHTABLE_KEY_TYPE *);
  59. #endif
  60. // First layer of macro for token concatenation
  61. #define CONCAT_INTERNAL(a, b) a ## b
  62. // Second layer of macro, which ensures proper expansion
  63. #define CONCAT(a, b) CONCAT_INTERNAL(a, b)
  64. // define names for all structures and structures
  65. #define simple_hashtable_init_named CONCAT(simple_hashtable_init, SIMPLE_HASHTABLE_NAME)
  66. #define simple_hashtable_destroy_named CONCAT(simple_hashtable_destroy, SIMPLE_HASHTABLE_NAME)
  67. #define simple_hashtable_slot_named CONCAT(simple_hashtable_slot, SIMPLE_HASHTABLE_NAME)
  68. #define SIMPLE_HASHTABLE_SLOT_NAMED CONCAT(SIMPLE_HASHTABLE_SLOT, SIMPLE_HASHTABLE_NAME)
  69. #define simple_hashtable_named CONCAT(simple_hashtable, SIMPLE_HASHTABLE_NAME)
  70. #define SIMPLE_HASHTABLE_NAMED CONCAT(SIMPLE_HASHTABLE, SIMPLE_HASHTABLE_NAME)
  71. #define simple_hashtable_resize_named CONCAT(simple_hashtable_resize, SIMPLE_HASHTABLE_NAME)
  72. #define simple_hashtable_can_use_slot_named CONCAT(simple_hashtable_keys_match, SIMPLE_HASHTABLE_NAME)
  73. #define simple_hashtable_get_slot_named CONCAT(simple_hashtable_get_slot, SIMPLE_HASHTABLE_NAME)
  74. #define simple_hashtable_del_slot_named CONCAT(simple_hashtable_del_slot, SIMPLE_HASHTABLE_NAME)
  75. #define simple_hashtable_set_slot_named CONCAT(simple_hashtable_set_slot, SIMPLE_HASHTABLE_NAME)
  76. #define simple_hashtable_first_read_only_named CONCAT(simple_hashtable_first_read_only, SIMPLE_HASHTABLE_NAME)
  77. #define simple_hashtable_next_read_only_named CONCAT(simple_hashtable_next_read_only, SIMPLE_HASHTABLE_NAME)
  78. #define simple_hashtable_sorted_binary_search_named CONCAT(simple_hashtable_sorted_binary_search, SIMPLE_HASHTABLE_NAME)
  79. #define simple_hashtable_add_value_sorted_named CONCAT(simple_hashtable_add_value_sorted, SIMPLE_HASHTABLE_NAME)
  80. #define simple_hashtable_del_value_sorted_named CONCAT(simple_hashtable_del_value_sorted, SIMPLE_HASHTABLE_NAME)
  81. #define simple_hashtable_replace_value_sorted_named CONCAT(simple_hashtable_replace_value_sorted, SIMPLE_HASHTABLE_NAME)
  82. #define simple_hashtable_sorted_array_first_read_only_named CONCAT(simple_hashtable_sorted_array_first_read_only, SIMPLE_HASHTABLE_NAME)
  83. #define simple_hashtable_sorted_array_next_read_only_named CONCAT(simple_hashtable_sorted_array_next_read_only, SIMPLE_HASHTABLE_NAME)
  84. typedef struct simple_hashtable_slot_named {
  85. SIMPLE_HASHTABLE_HASH hash;
  86. SIMPLE_HASHTABLE_VALUE_TYPE *data;
  87. } SIMPLE_HASHTABLE_SLOT_NAMED;
  88. typedef struct simple_hashtable_named {
  89. size_t resizes;
  90. size_t searches;
  91. size_t collisions;
  92. size_t additions;
  93. size_t deletions;
  94. size_t deleted;
  95. size_t used;
  96. size_t size;
  97. bool needs_cleanup;
  98. SIMPLE_HASHTABLE_SLOT_NAMED *hashtable;
  99. #ifdef SIMPLE_HASHTABLE_SORT_FUNCTION
  100. struct {
  101. size_t used;
  102. size_t size;
  103. SIMPLE_HASHTABLE_VALUE_TYPE **array;
  104. } sorted;
  105. #endif
  106. } SIMPLE_HASHTABLE_NAMED;
  107. #ifdef SIMPLE_HASHTABLE_SORT_FUNCTION
  108. static inline size_t simple_hashtable_sorted_binary_search_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_VALUE_TYPE *value) {
  109. size_t left = 0, right = ht->sorted.used;
  110. while (left < right) {
  111. size_t mid = left + (right - left) / 2;
  112. if (SIMPLE_HASHTABLE_SORT_FUNCTION(ht->sorted.array[mid], value) < 0)
  113. left = mid + 1;
  114. else
  115. right = mid;
  116. }
  117. return left;
  118. }
  119. static inline void simple_hashtable_add_value_sorted_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_VALUE_TYPE *value) {
  120. size_t index = simple_hashtable_sorted_binary_search_named(ht, value);
  121. // Ensure there's enough space in the sorted array
  122. if (ht->sorted.used >= ht->sorted.size) {
  123. size_t size = ht->sorted.size ? ht->sorted.size * 2 : 64;
  124. SIMPLE_HASHTABLE_VALUE_TYPE **array = mallocz(size * sizeof(SIMPLE_HASHTABLE_VALUE_TYPE *));
  125. if(ht->sorted.array) {
  126. memcpy(array, ht->sorted.array, ht->sorted.size * sizeof(SIMPLE_HASHTABLE_VALUE_TYPE *));
  127. freez(ht->sorted.array);
  128. }
  129. ht->sorted.array = array;
  130. ht->sorted.size = size;
  131. }
  132. // Use memmove to shift elements and create space for the new element
  133. memmove(&ht->sorted.array[index + 1], &ht->sorted.array[index], (ht->sorted.used - index) * sizeof(SIMPLE_HASHTABLE_VALUE_TYPE *));
  134. ht->sorted.array[index] = value;
  135. ht->sorted.used++;
  136. }
  137. static inline void simple_hashtable_del_value_sorted_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_VALUE_TYPE *value) {
  138. size_t index = simple_hashtable_sorted_binary_search_named(ht, value);
  139. // Check if the value exists at the found index
  140. assert(index < ht->sorted.used && ht->sorted.array[index] == value);
  141. // Use memmove to shift elements and close the gap
  142. memmove(&ht->sorted.array[index], &ht->sorted.array[index + 1], (ht->sorted.used - index - 1) * sizeof(SIMPLE_HASHTABLE_VALUE_TYPE *));
  143. ht->sorted.used--;
  144. }
  145. static inline void simple_hashtable_replace_value_sorted_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_VALUE_TYPE *old_value, SIMPLE_HASHTABLE_VALUE_TYPE *new_value) {
  146. if(new_value == old_value)
  147. return;
  148. size_t old_value_index = simple_hashtable_sorted_binary_search_named(ht, old_value);
  149. assert(old_value_index < ht->sorted.used && ht->sorted.array[old_value_index] == old_value);
  150. int r = SIMPLE_HASHTABLE_SORT_FUNCTION(old_value, new_value);
  151. if(r == 0) {
  152. // Same value, so use the same index
  153. ht->sorted.array[old_value_index] = new_value;
  154. return;
  155. }
  156. size_t new_value_index = simple_hashtable_sorted_binary_search_named(ht, new_value);
  157. if(old_value_index == new_value_index) {
  158. // Not the same value, but still at the same index
  159. ht->sorted.array[old_value_index] = new_value;
  160. return;
  161. }
  162. else if (old_value_index < new_value_index) {
  163. // The old value is before the new value
  164. size_t shift_start = old_value_index + 1;
  165. size_t shift_end = new_value_index - 1;
  166. size_t shift_size = shift_end - old_value_index;
  167. memmove(&ht->sorted.array[old_value_index], &ht->sorted.array[shift_start], shift_size * sizeof(SIMPLE_HASHTABLE_VALUE_TYPE *));
  168. ht->sorted.array[shift_end] = new_value;
  169. }
  170. else {
  171. // The old value is after the new value
  172. size_t shift_start = new_value_index;
  173. size_t shift_end = old_value_index;
  174. size_t shift_size = shift_end - new_value_index;
  175. memmove(&ht->sorted.array[new_value_index + 1], &ht->sorted.array[shift_start], shift_size * sizeof(SIMPLE_HASHTABLE_VALUE_TYPE *));
  176. ht->sorted.array[new_value_index] = new_value;
  177. }
  178. }
  179. static inline SIMPLE_HASHTABLE_VALUE_TYPE **simple_hashtable_sorted_array_first_read_only_named(SIMPLE_HASHTABLE_NAMED *ht) {
  180. if (ht->sorted.used > 0) {
  181. return &ht->sorted.array[0];
  182. }
  183. return NULL;
  184. }
  185. static inline SIMPLE_HASHTABLE_VALUE_TYPE **simple_hashtable_sorted_array_next_read_only_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_VALUE_TYPE **last) {
  186. if (!last) return NULL;
  187. // Calculate the current position in the sorted array
  188. size_t currentIndex = last - ht->sorted.array;
  189. // Proceed to the next element if it exists
  190. if (currentIndex + 1 < ht->sorted.used) {
  191. return &ht->sorted.array[currentIndex + 1];
  192. }
  193. // If no more elements, return NULL
  194. return NULL;
  195. }
  196. #define SIMPLE_HASHTABLE_SORTED_FOREACH_READ_ONLY(ht, var, type, name) \
  197. for (type **(var) = simple_hashtable_sorted_array_first_read_only ## name(ht); \
  198. var; \
  199. (var) = simple_hashtable_sorted_array_next_read_only ## name(ht, var))
  200. #define SIMPLE_HASHTABLE_SORTED_FOREACH_READ_ONLY_VALUE(var) (*(var))
  201. #else
  202. static inline void simple_hashtable_add_value_sorted_named(SIMPLE_HASHTABLE_NAMED *ht __maybe_unused, SIMPLE_HASHTABLE_VALUE_TYPE *value __maybe_unused) { ; }
  203. static inline void simple_hashtable_del_value_sorted_named(SIMPLE_HASHTABLE_NAMED *ht __maybe_unused, SIMPLE_HASHTABLE_VALUE_TYPE *value __maybe_unused) { ; }
  204. static inline void simple_hashtable_replace_value_sorted_named(SIMPLE_HASHTABLE_NAMED *ht __maybe_unused, SIMPLE_HASHTABLE_VALUE_TYPE *old_value __maybe_unused, SIMPLE_HASHTABLE_VALUE_TYPE *new_value __maybe_unused) { ; }
  205. #endif
  206. static inline void simple_hashtable_init_named(SIMPLE_HASHTABLE_NAMED *ht, size_t size) {
  207. memset(ht, 0, sizeof(*ht));
  208. ht->size = size;
  209. ht->hashtable = callocz(ht->size, sizeof(*ht->hashtable));
  210. }
  211. static inline void simple_hashtable_destroy_named(SIMPLE_HASHTABLE_NAMED *ht) {
  212. #ifdef SIMPLE_HASHTABLE_SORT_FUNCTION
  213. freez(ht->sorted.array);
  214. #endif
  215. freez(ht->hashtable);
  216. memset(ht, 0, sizeof(*ht));
  217. }
  218. static inline void simple_hashtable_resize_named(SIMPLE_HASHTABLE_NAMED *ht);
  219. #define simple_hashtable_data_unset ((void *)NULL)
  220. #define simple_hashtable_data_deleted ((void *)UINT64_MAX)
  221. #define simple_hashtable_data_usernull ((void *)(UINT64_MAX - 1))
  222. #define simple_hashtable_is_slot_unset(sl) ((sl)->data == simple_hashtable_data_unset)
  223. #define simple_hashtable_is_slot_deleted(sl) ((sl)->data == simple_hashtable_data_deleted)
  224. #define simple_hashtable_is_slot_usernull(sl) ((sl)->data == simple_hashtable_data_usernull)
  225. #define SIMPLE_HASHTABLE_SLOT_DATA(sl) ((simple_hashtable_is_slot_unset(sl) || simple_hashtable_is_slot_deleted(sl) || simple_hashtable_is_slot_usernull(sl)) ? NULL : (sl)->data)
  226. static inline bool simple_hashtable_can_use_slot_named(
  227. SIMPLE_HASHTABLE_SLOT_NAMED *sl, SIMPLE_HASHTABLE_HASH hash,
  228. SIMPLE_HASHTABLE_KEY_TYPE *key __maybe_unused) {
  229. if(simple_hashtable_is_slot_unset(sl))
  230. return true;
  231. if(simple_hashtable_is_slot_deleted(sl))
  232. return false;
  233. if(sl->hash == hash) {
  234. #if defined(SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION) && defined(SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION)
  235. return SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION(SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION(SIMPLE_HASHTABLE_SLOT_DATA(sl)), key);
  236. #else
  237. return true;
  238. #endif
  239. }
  240. return false;
  241. }
  242. #define SIMPLE_HASHTABLE_NEEDS_RESIZE(ht) ((ht)->size <= ((ht)->used - (ht)->deleted) << 1 || (ht)->used >= (ht)->size)
  243. // IMPORTANT: the pointer returned by this call is valid up to the next call of this function (or the resize one).
  244. // If you need to cache something, cache the hash, not the slot pointer.
  245. static inline SIMPLE_HASHTABLE_SLOT_NAMED *simple_hashtable_get_slot_named(
  246. SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_HASH hash,
  247. SIMPLE_HASHTABLE_KEY_TYPE *key, bool resize) {
  248. // This function finds the requested hash and key in the hashtable.
  249. // It uses a second version of the hash in case of collisions, and then linear probing.
  250. // It may resize the hashtable if it is more than 50% full.
  251. // Deleted items remain in the hashtable, but they are marked as DELETED.
  252. // Reuse of DELETED slots happens only if the slot to be returned is UNSET.
  253. // So, when looking up for an item, it tries to find it, assuming DELETED
  254. // slots are occupied. If the item to be returned is UNSET, and it has
  255. // encountered a DELETED slot, it returns the DELETED one instead of the UNSET.
  256. ht->searches++;
  257. size_t slot;
  258. SIMPLE_HASHTABLE_SLOT_NAMED *sl;
  259. SIMPLE_HASHTABLE_SLOT_NAMED *deleted;
  260. slot = hash % ht->size;
  261. sl = &ht->hashtable[slot];
  262. deleted = simple_hashtable_is_slot_deleted(sl) ? sl : NULL;
  263. if(likely(simple_hashtable_can_use_slot_named(sl, hash, key)))
  264. return (simple_hashtable_is_slot_unset(sl) && deleted) ? deleted : sl;
  265. ht->collisions++;
  266. if(unlikely(resize && (ht->needs_cleanup || SIMPLE_HASHTABLE_NEEDS_RESIZE(ht)))) {
  267. simple_hashtable_resize_named(ht);
  268. deleted = NULL; // our deleted pointer is not valid anymore
  269. slot = hash % ht->size;
  270. sl = &ht->hashtable[slot];
  271. if(likely(simple_hashtable_can_use_slot_named(sl, hash, key)))
  272. return sl;
  273. ht->collisions++;
  274. }
  275. slot = ((hash >> SIMPLE_HASHTABLE_HASH_SECOND_HASH_SHIFTS) + 1) % ht->size;
  276. sl = &ht->hashtable[slot];
  277. deleted = (!deleted && simple_hashtable_is_slot_deleted(sl)) ? sl : deleted;
  278. // Linear probing until we find it
  279. SIMPLE_HASHTABLE_SLOT_NAMED *sl_started = sl;
  280. size_t collisions_started = ht->collisions;
  281. while (!simple_hashtable_can_use_slot_named(sl, hash, key)) {
  282. slot = (slot + 1) % ht->size; // Wrap around if necessary
  283. sl = &ht->hashtable[slot];
  284. deleted = (!deleted && simple_hashtable_is_slot_deleted(sl)) ? sl : deleted;
  285. ht->collisions++;
  286. if(sl == sl_started) {
  287. if(deleted) {
  288. // we looped through all items, and we didn't find a free slot,
  289. // but we have found a deleted slot, so return it.
  290. return deleted;
  291. }
  292. else if(resize) {
  293. // the hashtable is full, without any deleted slots.
  294. // we need to resize it now.
  295. simple_hashtable_resize_named(ht);
  296. return simple_hashtable_get_slot_named(ht, hash, key, false);
  297. }
  298. else {
  299. // the hashtable is full, but resize is false.
  300. // this should never happen.
  301. assert(sl != sl_started);
  302. }
  303. }
  304. }
  305. if((ht->collisions - collisions_started) > (ht->size / 2) && ht->deleted >= (ht->size / 3)) {
  306. // we traversed through half of the hashtable to find a slot,
  307. // but we have more than 1/3 deleted items
  308. ht->needs_cleanup = true;
  309. }
  310. return (simple_hashtable_is_slot_unset(sl) && deleted) ? deleted : sl;
  311. }
  312. static inline bool simple_hashtable_del_slot_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_SLOT_NAMED *sl) {
  313. if(simple_hashtable_is_slot_unset(sl) || simple_hashtable_is_slot_deleted(sl))
  314. return false;
  315. ht->deletions++;
  316. ht->deleted++;
  317. simple_hashtable_del_value_sorted_named(ht, SIMPLE_HASHTABLE_SLOT_DATA(sl));
  318. sl->data = simple_hashtable_data_deleted;
  319. return true;
  320. }
  321. static inline void simple_hashtable_set_slot_named(
  322. SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_SLOT_NAMED *sl,
  323. SIMPLE_HASHTABLE_HASH hash, SIMPLE_HASHTABLE_VALUE_TYPE *data) {
  324. if(data == NULL)
  325. data = simple_hashtable_data_usernull;
  326. if(unlikely(data == simple_hashtable_data_unset || data == simple_hashtable_data_deleted)) {
  327. simple_hashtable_del_slot_named(ht, sl);
  328. return;
  329. }
  330. if(likely(simple_hashtable_is_slot_unset(sl))) {
  331. simple_hashtable_add_value_sorted_named(ht, data);
  332. ht->used++;
  333. }
  334. else if(unlikely(simple_hashtable_is_slot_deleted(sl))) {
  335. ht->deleted--;
  336. }
  337. else
  338. simple_hashtable_replace_value_sorted_named(ht, SIMPLE_HASHTABLE_SLOT_DATA(sl), data);
  339. sl->hash = hash;
  340. sl->data = data;
  341. ht->additions++;
  342. }
  343. // IMPORTANT
  344. // this call invalidates all SIMPLE_HASHTABLE_SLOT_NAMED pointers
  345. static inline void simple_hashtable_resize_named(SIMPLE_HASHTABLE_NAMED *ht) {
  346. SIMPLE_HASHTABLE_SLOT_NAMED *old = ht->hashtable;
  347. size_t old_size = ht->size;
  348. size_t new_size = ht->size;
  349. if(SIMPLE_HASHTABLE_NEEDS_RESIZE(ht))
  350. new_size = (ht->size << 1) - ((ht->size > 16) ? 1 : 0);
  351. ht->resizes++;
  352. ht->size = new_size;
  353. ht->hashtable = callocz(new_size, sizeof(*ht->hashtable));
  354. size_t used = 0;
  355. for(size_t i = 0 ; i < old_size ; i++) {
  356. SIMPLE_HASHTABLE_SLOT_NAMED *slot = &old[i];
  357. if(simple_hashtable_is_slot_unset(slot) || simple_hashtable_is_slot_deleted(slot))
  358. continue;
  359. SIMPLE_HASHTABLE_KEY_TYPE *key = NULL;
  360. #if defined(SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION) && defined(SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION)
  361. SIMPLE_HASHTABLE_VALUE_TYPE *value = SIMPLE_HASHTABLE_SLOT_DATA(slot);
  362. key = SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION(value);
  363. #endif
  364. SIMPLE_HASHTABLE_SLOT_NAMED *slot2 = simple_hashtable_get_slot_named(ht, slot->hash, key, false);
  365. *slot2 = *slot;
  366. used++;
  367. }
  368. assert(used == ht->used - ht->deleted);
  369. ht->used = used;
  370. ht->deleted = 0;
  371. ht->needs_cleanup = false;
  372. freez(old);
  373. }
  374. // ----------------------------------------------------------------------------
  375. // hashtable traversal, in read-only mode
  376. // the hashtable should not be modified while the traversal is taking place
  377. static inline SIMPLE_HASHTABLE_SLOT_NAMED *simple_hashtable_first_read_only_named(SIMPLE_HASHTABLE_NAMED *ht) {
  378. for(size_t i = 0; i < ht->size ;i++) {
  379. SIMPLE_HASHTABLE_SLOT_NAMED *sl = &ht->hashtable[i];
  380. if(!simple_hashtable_is_slot_unset(sl) && !simple_hashtable_is_slot_deleted(sl))
  381. return sl;
  382. }
  383. return NULL;
  384. }
  385. static inline SIMPLE_HASHTABLE_SLOT_NAMED *simple_hashtable_next_read_only_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_SLOT_NAMED *last) {
  386. if (!last) return NULL;
  387. // Calculate the current position in the array
  388. size_t index = last - ht->hashtable;
  389. // Iterate over the hashtable starting from the next element
  390. for (size_t i = index + 1; i < ht->size; i++) {
  391. SIMPLE_HASHTABLE_SLOT_NAMED *sl = &ht->hashtable[i];
  392. if (!simple_hashtable_is_slot_unset(sl) && !simple_hashtable_is_slot_deleted(sl)) {
  393. return sl;
  394. }
  395. }
  396. // If no more data slots are found, return NULL
  397. return NULL;
  398. }
  399. #define SIMPLE_HASHTABLE_FOREACH_READ_ONLY(ht, var, name) \
  400. for(struct simple_hashtable_slot ## name *(var) = simple_hashtable_first_read_only ## name(ht); \
  401. var; \
  402. (var) = simple_hashtable_next_read_only ## name(ht, var))
  403. #define SIMPLE_HASHTABLE_FOREACH_READ_ONLY_VALUE(var) SIMPLE_HASHTABLE_SLOT_DATA(var)
  404. // ----------------------------------------------------------------------------
  405. // high level implementation
  406. #ifdef SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION
  407. #ifndef XXH_INLINE_ALL
  408. #define XXH_INLINE_ALL
  409. #endif
  410. #include "xxhash.h"
  411. #define simple_hashtable_set_named CONCAT(simple_hashtable_set, SIMPLE_HASHTABLE_NAME)
  412. #define simple_hashtable_get_named CONCAT(simple_hashtable_get, SIMPLE_HASHTABLE_NAME)
  413. #define simple_hashtable_del_named CONCAT(simple_hashtable_del, SIMPLE_HASHTABLE_NAME)
  414. static inline SIMPLE_HASHTABLE_VALUE_TYPE *simple_hashtable_set_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_KEY_TYPE *key, size_t key_len, SIMPLE_HASHTABLE_VALUE_TYPE *data) {
  415. XXH64_hash_t hash = XXH3_64bits((void *)key, key_len);
  416. SIMPLE_HASHTABLE_SLOT_NAMED *sl = simple_hashtable_get_slot_named(ht, hash, key, true);
  417. simple_hashtable_set_slot_named(ht, sl, hash, data);
  418. return SIMPLE_HASHTABLE_SLOT_DATA(sl);
  419. }
  420. static inline SIMPLE_HASHTABLE_VALUE_TYPE *simple_hashtable_get_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_KEY_TYPE *key, size_t key_len, SIMPLE_HASHTABLE_VALUE_TYPE *data) {
  421. XXH64_hash_t hash = XXH3_64bits((void *)key, key_len);
  422. SIMPLE_HASHTABLE_SLOT_NAMED *sl = simple_hashtable_get_slot_named(ht, hash, key, true);
  423. return SIMPLE_HASHTABLE_SLOT_DATA(sl);
  424. }
  425. static inline bool simple_hashtable_del_named(SIMPLE_HASHTABLE_NAMED *ht, SIMPLE_HASHTABLE_KEY_TYPE *key, size_t key_len, SIMPLE_HASHTABLE_VALUE_TYPE *data) {
  426. XXH64_hash_t hash = XXH3_64bits((void *)key, key_len);
  427. SIMPLE_HASHTABLE_SLOT_NAMED *sl = simple_hashtable_get_slot_named(ht, hash, key, true);
  428. return simple_hashtable_del_slot_named(ht, sl);
  429. }
  430. #endif // SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION
  431. // ----------------------------------------------------------------------------
  432. // Clear the preprocessor defines of simple_hashtable.h
  433. // allowing simple_hashtable.h to be included multiple times
  434. // with different configuration each time.
  435. #include "simple_hashtable_undef.h"
  436. #endif //NETDATA_SIMPLE_HASHTABLE_H