hash_table.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /* hash_table.c: hash table implementation
  2. *
  3. */
  4. #include "common.h"
  5. #include "hash_table.h"
  6. static unsigned int my_string_hash(const void *key);
  7. static unsigned int my_int_hash(const void *key);
  8. static unsigned int my_mixed_hash (const void *key);
  9. static int my_int_cmp(const void *left, const void *right);
  10. hash_table_t *hash_table_create(hash_table_hash_alg alg, hash_table_cmp_alg cmp, int nrows, int flags)
  11. {
  12. hash_table_t *ht = NULL;
  13. if (nrows <= 0) nrows = 13; /* Give them a small table to start with. */
  14. ht = (hash_table_t *) my_calloc(1, sizeof(*ht));
  15. ht->rows = (hash_table_row_t *) my_calloc(nrows, sizeof(*ht->rows));
  16. if (alg) ht->hash = alg;
  17. else {
  18. if (flags & HASH_TABLE_STRINGS) ht->hash = my_string_hash;
  19. else if (flags & HASH_TABLE_INTS) ht->hash = my_int_hash;
  20. else ht->hash = my_mixed_hash;
  21. }
  22. if (cmp) ht->cmp = cmp;
  23. else {
  24. if (flags & HASH_TABLE_INTS) ht->cmp = my_int_cmp;
  25. else ht->cmp = (hash_table_cmp_alg) strcmp;
  26. }
  27. ht->flags = flags;
  28. ht->max_rows = nrows;
  29. return(ht);
  30. }
  31. int hash_table_delete(hash_table_t *ht)
  32. {
  33. hash_table_entry_t *entry = NULL, *next = NULL;
  34. hash_table_row_t *row = NULL;
  35. int i;
  36. if (!ht) return(-1);
  37. for (i = 0; i < ht->max_rows; i++) {
  38. row = ht->rows+i;
  39. for (entry = row->head; entry; entry = next) {
  40. next = entry->next;
  41. free(entry);
  42. }
  43. }
  44. free(ht->rows);
  45. free(ht);
  46. return(0);
  47. }
  48. int hash_table_check_resize(hash_table_t *ht)
  49. {
  50. if (!ht) return(-1);
  51. /* This 100 allows (ht->max_rows) linked lists each with an average of 100 elements in the list
  52. * before actually resizing.
  53. * This is done to avoid a very slow and cpu intensive resize which requires recalculating all hashes.
  54. * Having (ht->max_rows) linked lists is still more effecient than one large linked list.
  55. */
  56. if (ht->cells_in_use / ht->max_rows > 100) {
  57. hash_table_resize(ht, ht->max_rows * 3);
  58. }
  59. return(0);
  60. }
  61. int hash_table_resize(hash_table_t *ht, int nrows)
  62. {
  63. int i, newidx;
  64. hash_table_row_t *oldrow = NULL, *newrows = NULL;
  65. hash_table_entry_t *entry = NULL, *next = NULL;
  66. if (!ht) return(-1);
  67. /* First allocate the new rows. */
  68. newrows = (hash_table_row_t *) my_calloc(nrows, sizeof(*newrows));
  69. /* Now populate it with the old entries. */
  70. for (i = 0; i < ht->max_rows; i++) {
  71. oldrow = ht->rows+i;
  72. for (entry = oldrow->head; entry; entry = next) {
  73. next = entry->next;
  74. newidx = entry->hash % nrows;
  75. entry->next = newrows[newidx].head;
  76. newrows[newidx].head = entry;
  77. newrows[newidx].len++;
  78. }
  79. }
  80. free(ht->rows);
  81. ht->rows = newrows;
  82. ht->max_rows = nrows;
  83. return(0);
  84. }
  85. int hash_table_insert(hash_table_t *ht, const void *key, void *data)
  86. {
  87. unsigned int hash;
  88. int idx;
  89. hash_table_entry_t *entry = NULL;
  90. hash_table_row_t *row = NULL;
  91. if (!ht) return(-1);
  92. hash = ht->hash(key);
  93. idx = hash % ht->max_rows;
  94. row = ht->rows+idx;
  95. /* Allocate an entry. */
  96. entry = (hash_table_entry_t *) my_calloc(1, sizeof(*entry));
  97. entry->key = key;
  98. entry->data = data;
  99. entry->hash = hash;
  100. /* Insert it into the list. */
  101. entry->next = row->head;
  102. row->head = entry;
  103. /* Update stats. */
  104. row->len++;
  105. ht->cells_in_use++;
  106. /* See if we need to update the table. */
  107. if (!(ht->flags & HASH_TABLE_NORESIZE)) {
  108. hash_table_check_resize(ht);
  109. }
  110. return(0);
  111. }
  112. int hash_table_find(hash_table_t *ht, const void *key, void *dataptr)
  113. {
  114. int idx;
  115. unsigned int hash;
  116. hash_table_entry_t *entry = NULL;
  117. hash_table_row_t *row = NULL;
  118. if (!ht) return (-1);
  119. hash = ht->hash(key);
  120. idx = hash % ht->max_rows;
  121. row = ht->rows+idx;
  122. for (entry = row->head; entry; entry = entry->next) {
  123. if (hash == entry->hash && !ht->cmp(key, entry->key)) {
  124. *(void **)dataptr = entry->data;
  125. return(0);
  126. }
  127. }
  128. return(-1);
  129. }
  130. int hash_table_remove(hash_table_t *ht, const void *key, void *dataptr)
  131. {
  132. int idx;
  133. unsigned int hash;
  134. hash_table_entry_t *entry = NULL, *last = NULL;
  135. hash_table_row_t *row = NULL;
  136. if (!ht) return(-1);
  137. hash = ht->hash(key);
  138. idx = hash % ht->max_rows;
  139. row = ht->rows+idx;
  140. last = NULL;
  141. for (entry = row->head; entry; entry = entry->next) {
  142. if (hash == entry->hash && !ht->cmp(key, entry->key)) {
  143. if (dataptr) *(void **)dataptr = entry->data;
  144. /* Remove it from the row's list. */
  145. if (last) last->next = entry->next;
  146. else row->head = entry->next;
  147. free(entry);
  148. ht->cells_in_use--;
  149. return(0);
  150. }
  151. last = entry;
  152. }
  153. return(-1);
  154. }
  155. int hash_table_walk(hash_table_t *ht, hash_table_node_func callback, void *param)
  156. {
  157. if (!ht) return(-1);
  158. hash_table_row_t *row = NULL;
  159. hash_table_entry_t *entry = NULL, *next = NULL;
  160. int i;
  161. for (i = 0; i < ht->max_rows; i++) {
  162. row = ht->rows+i;
  163. for (entry = row->head; entry;) {
  164. next = entry->next;
  165. callback(entry->key, &entry->data, param);
  166. entry = next;
  167. }
  168. }
  169. return(0);
  170. }
  171. static int my_int_cmp(const void *left, const void *right)
  172. {
  173. return((int) left - (int) right);
  174. }
  175. static unsigned int my_string_hash(const void *key)
  176. {
  177. int hash, loop;
  178. size_t keylen;
  179. const unsigned char *k = NULL;
  180. #define HASHC hash = *k++ + 65599 * hash
  181. hash = 0;
  182. k = (const unsigned char *) key;
  183. keylen = strlen((const char *) key);
  184. if (!keylen) return(0);
  185. loop = (keylen + 8 - 1) >> 3;
  186. switch (keylen & (8 - 1)) {
  187. case 0:
  188. do {
  189. HASHC;
  190. case 7:
  191. HASHC;
  192. case 6:
  193. HASHC;
  194. case 5:
  195. HASHC;
  196. case 4:
  197. HASHC;
  198. case 3:
  199. HASHC;
  200. case 2:
  201. HASHC;
  202. case 1:
  203. HASHC;
  204. } while (--loop);
  205. }
  206. return(hash);
  207. }
  208. static unsigned int my_int_hash(const void *key)
  209. {
  210. return((unsigned int)key);
  211. }
  212. static unsigned int my_mixed_hash (const void *key)
  213. {
  214. const unsigned char *k = NULL;
  215. unsigned int hash;
  216. k = (const unsigned char *) key;
  217. hash = 0;
  218. while (*k) {
  219. hash *= 16777619;
  220. hash ^= *k++;
  221. }
  222. return(hash);
  223. }