c_rhash.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Copyright: SPDX-License-Identifier: GPL-3.0-only
  2. #include "c_rhash_internal.h"
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #ifdef DEBUG_VERBOSE
  6. #include <stdio.h>
  7. #endif
  8. #define c_rmalloc(...) malloc(__VA_ARGS__)
  9. #define c_rcalloc(...) calloc(__VA_ARGS__)
  10. #define c_rfree(...) free(__VA_ARGS__)
  11. static inline uint32_t simple_hash(const char *name) {
  12. unsigned char *s = (unsigned char *) name;
  13. uint32_t hval = 0x811c9dc5;
  14. while (*s) {
  15. hval *= 16777619;
  16. hval ^= (uint32_t) *s++;
  17. }
  18. return hval;
  19. }
  20. c_rhash c_rhash_new(size_t bin_count) {
  21. if (!bin_count)
  22. bin_count = 1000;
  23. c_rhash hash = c_rcalloc(1, sizeof(struct c_rhash_s) + (bin_count * sizeof(struct bin_ll*)) );
  24. if (hash == NULL)
  25. return NULL;
  26. hash->bin_count = bin_count;
  27. hash->bins = (c_rhash_bin *)((char*)hash + sizeof(struct c_rhash_s));
  28. return hash;
  29. }
  30. static size_t get_itemtype_len(uint8_t item_type, const void* item_data) {
  31. switch (item_type) {
  32. case ITEMTYPE_STRING:
  33. return strlen(item_data) + 1;
  34. case ITEMTYPE_UINT64:
  35. return sizeof(uint64_t);
  36. case ITEMTYPE_UINT8:
  37. return 1;
  38. case ITEMTYPE_OPAQUE_PTR:
  39. return sizeof(void*);
  40. default:
  41. return 0;
  42. }
  43. }
  44. static int compare_bin_item(struct bin_item *item, uint8_t key_type, const void *key) {
  45. if (item->key_type != key_type)
  46. return 1;
  47. size_t key_value_len = get_itemtype_len(key_type, key);
  48. if(key_type == ITEMTYPE_STRING) {
  49. size_t new_key_value_len = get_itemtype_len(item->key_type, item->key);
  50. if (new_key_value_len != key_value_len)
  51. return 1;
  52. }
  53. if(memcmp(item->key, key, key_value_len) == 0) {
  54. return 0;
  55. }
  56. return 1;
  57. }
  58. static int insert_into_bin(c_rhash_bin *bin, uint8_t key_type, const void *key, uint8_t value_type, const void *value) {
  59. struct bin_item *prev = NULL;
  60. while (*bin != NULL) {
  61. if (!compare_bin_item(*bin, key_type, key)) {
  62. #ifdef DEBUG_VERBOSE
  63. printf("Key already present! Updating value!\n");
  64. #endif
  65. // TODO: optimize here if the new value is of different kind compared to the old one
  66. // in case it is not crazily bigger we can reuse the memory and avoid malloc and free
  67. c_rfree((*bin)->value);
  68. (*bin)->value_type = value_type;
  69. (*bin)->value = c_rmalloc(get_itemtype_len(value_type, value));
  70. if ((*bin)->value == NULL)
  71. return 1;
  72. memcpy((*bin)->value, value, get_itemtype_len(value_type, value));
  73. return 0;
  74. }
  75. prev = *bin;
  76. bin = &(*bin)->next;
  77. }
  78. if (*bin == NULL)
  79. *bin = c_rcalloc(1, sizeof(struct bin_item));
  80. if (prev != NULL)
  81. prev->next = *bin;
  82. (*bin)->key_type = key_type;
  83. size_t len = get_itemtype_len(key_type, key);
  84. (*bin)->key = c_rmalloc(len);
  85. memcpy((*bin)->key, key, len);
  86. (*bin)->value_type = value_type;
  87. len = get_itemtype_len(value_type, value);
  88. (*bin)->value = c_rmalloc(len);
  89. memcpy((*bin)->value, value, len);
  90. return 0;
  91. }
  92. static inline uint32_t get_bin_idx_str(c_rhash hash, const char *key) {
  93. uint32_t nhash = simple_hash(key);
  94. return nhash % hash->bin_count;
  95. }
  96. static inline c_rhash_bin *get_binptr_by_str(c_rhash hash, const char *key) {
  97. return &hash->bins[get_bin_idx_str(hash, key)];
  98. }
  99. int c_rhash_insert_str_ptr(c_rhash hash, const char *key, void *value) {
  100. c_rhash_bin *bin = get_binptr_by_str(hash, key);
  101. #ifdef DEBUG_VERBOSE
  102. if (bin != NULL)
  103. printf("COLLISION. There will be more than one item in bin idx=%d\n", nhash);
  104. #endif
  105. return insert_into_bin(bin, ITEMTYPE_STRING, key, ITEMTYPE_OPAQUE_PTR, &value);
  106. }
  107. int c_rhash_insert_str_uint8(c_rhash hash, const char *key, uint8_t value) {
  108. c_rhash_bin *bin = get_binptr_by_str(hash, key);
  109. #ifdef DEBUG_VERBOSE
  110. if (bin != NULL)
  111. printf("COLLISION. There will be more than one item in bin idx=%d\n", nhash);
  112. #endif
  113. return insert_into_bin(bin, ITEMTYPE_STRING, key, ITEMTYPE_UINT8, &value);
  114. }
  115. int c_rhash_insert_uint64_ptr(c_rhash hash, uint64_t key, void *value) {
  116. c_rhash_bin *bin = &hash->bins[key % hash->bin_count];
  117. #ifdef DEBUG_VERBOSE
  118. if (bin != NULL)
  119. printf("COLLISION. There will be more than one item in bin idx=%d\n", nhash);
  120. #endif
  121. return insert_into_bin(bin, ITEMTYPE_UINT64, &key, ITEMTYPE_OPAQUE_PTR, &value);
  122. }
  123. int c_rhash_get_uint8_by_str(c_rhash hash, const char *key, uint8_t *ret_val) {
  124. uint32_t nhash = get_bin_idx_str(hash, key);
  125. struct bin_item *bin = hash->bins[nhash];
  126. while (bin) {
  127. if (bin->key_type == ITEMTYPE_STRING) {
  128. if (!strcmp(bin->key, key)) {
  129. *ret_val = *(uint8_t*)bin->value;
  130. return 0;
  131. }
  132. }
  133. bin = bin->next;
  134. }
  135. return 1;
  136. }
  137. int c_rhash_get_ptr_by_str(c_rhash hash, const char *key, void **ret_val) {
  138. uint32_t nhash = get_bin_idx_str(hash, key);
  139. struct bin_item *bin = hash->bins[nhash];
  140. while (bin) {
  141. if (bin->key_type == ITEMTYPE_STRING) {
  142. if (!strcmp(bin->key, key)) {
  143. *ret_val = *((void**)bin->value);
  144. return 0;
  145. }
  146. }
  147. bin = bin->next;
  148. }
  149. *ret_val = NULL;
  150. return 1;
  151. }
  152. int c_rhash_get_ptr_by_uint64(c_rhash hash, uint64_t key, void **ret_val) {
  153. uint32_t nhash = key % hash->bin_count;
  154. struct bin_item *bin = hash->bins[nhash];
  155. while (bin) {
  156. if (bin->key_type == ITEMTYPE_UINT64) {
  157. if (*((uint64_t *)bin->key) == key) {
  158. *ret_val = *((void**)bin->value);
  159. return 0;
  160. }
  161. }
  162. bin = bin->next;
  163. }
  164. *ret_val = NULL;
  165. return 1;
  166. }
  167. static void c_rhash_destroy_bin(c_rhash_bin bin) {
  168. struct bin_item *next;
  169. do {
  170. next = bin->next;
  171. c_rfree(bin->key);
  172. c_rfree(bin->value);
  173. c_rfree(bin);
  174. bin = next;
  175. } while (bin != NULL);
  176. }
  177. int c_rhash_iter_uint64_keys(c_rhash hash, c_rhash_iter_t *iter, uint64_t *key) {
  178. while (iter->bin < hash->bin_count) {
  179. if (iter->item != NULL)
  180. iter->item = iter->item->next;
  181. if (iter->item == NULL) {
  182. if (iter->initialized)
  183. iter->bin++;
  184. else
  185. iter->initialized = 1;
  186. if (iter->bin < hash->bin_count)
  187. iter->item = hash->bins[iter->bin];
  188. }
  189. if (iter->item != NULL && iter->item->key_type == ITEMTYPE_UINT64) {
  190. *key = *(uint64_t*)iter->item->key;
  191. return 0;
  192. }
  193. }
  194. return 1;
  195. }
  196. int c_rhash_iter_str_keys(c_rhash hash, c_rhash_iter_t *iter, const char **key) {
  197. while (iter->bin < hash->bin_count) {
  198. if (iter->item != NULL)
  199. iter->item = iter->item->next;
  200. if (iter->item == NULL) {
  201. if (iter->initialized)
  202. iter->bin++;
  203. else
  204. iter->initialized = 1;
  205. if (iter->bin < hash->bin_count)
  206. iter->item = hash->bins[iter->bin];
  207. }
  208. if (iter->item != NULL && iter->item->key_type == ITEMTYPE_STRING) {
  209. *key = (const char*)iter->item->key;
  210. return 0;
  211. }
  212. }
  213. return 1;
  214. }
  215. void c_rhash_destroy(c_rhash hash) {
  216. for (size_t i = 0; i < hash->bin_count; i++) {
  217. if (hash->bins[i] != NULL)
  218. c_rhash_destroy_bin(hash->bins[i]);
  219. }
  220. c_rfree(hash);
  221. }