/ngtemplate/libuseful/hashtable.c

https://bitbucket.org/questor/mongoose-single-threaded · C · 258 lines · 145 code · 44 blank · 69 comment · 28 complexity · ae5166193cad60118c51f6f61698be1d MD5 · raw file

  1. /**
  2. * Hashtable implementation
  3. */
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "hashtable.h"
  7. typedef struct hashtable_iter_tag {
  8. hashtable* ht;
  9. int current_bucket;
  10. list_element* current;
  11. } hashtable_iter_impl;
  12. // Prototypes for some default functions if none other are given
  13. int hashpjw(const void* key);
  14. int matchstr(const void* key1, const void* key2);
  15. /**
  16. * Initializes the given hashtable, with the given number of buckets to hold values. Takes
  17. * pointers to functions for:
  18. * h: The hash function. If NULL, P.J. Weinberger's string hash function from the Dragon Book
  19. * will be used
  20. * match: The match function. Used to determine if two keys match. If NULL, !strcmp will be
  21. * used
  22. * destroy: The function to call when the value data of a key needs to be cleaned-up. Pass in 0
  23. * if you wish to take care of this yourself, or you could pass in free() if the objects are simple
  24. *
  25. * Returns 0 if the hashtable was initialized successfully, -1 otherwise
  26. */
  27. int ht_init(hashtable* ht, int buckets, int (*h)(const void* key),
  28. int (*match)(const void* key1, const void* key2),
  29. void (*destroy)(void *data)) {
  30. int i;
  31. if ((ht->table = (list*)malloc(buckets * sizeof(list))) == 0) {
  32. return -1;
  33. }
  34. ht->buckets = buckets;
  35. for (i = 0; i < ht->buckets; i++) {
  36. list_init(&ht->table[i], destroy);
  37. }
  38. ht->h = h? h : ht_hashpjw;
  39. ht->match = match? match : matchstr;
  40. ht->destroy = destroy;
  41. ht->size = 0;
  42. return 0;
  43. }
  44. /**
  45. * Destroys the given hashtable, calling the user-supplied "destroy" function on each value in the hash
  46. */
  47. void ht_destroy(hashtable* ht) {
  48. int i;
  49. for (i = 0; i < ht->buckets; i++) {
  50. list_destroy(&ht->table[i]);
  51. }
  52. free(ht->table);
  53. memset(ht, 0, sizeof(hashtable));
  54. }
  55. /**
  56. * Inserts a new value in the given hashtable
  57. *
  58. * Returns 0 if instering the element was successful, 1 if the element was already in the table,
  59. * -1 if there was a problem
  60. */
  61. int ht_insert(hashtable* ht, const void* data) {
  62. void* temp;
  63. int bucket, retval;
  64. temp = (void*)data;
  65. if (ht_lookup(ht, &temp) == 0) {
  66. // Do nothing, return 1 to signify that the element was already in the table
  67. return 1;
  68. }
  69. // Insert the data into the proper bucket
  70. bucket = ht->h(data) % ht->buckets;
  71. if ((retval = list_insert_next(&ht->table[bucket], NULL, data)) == 0) {
  72. ht->size++;
  73. }
  74. return retval;
  75. }
  76. /**
  77. * Removes an element from the given hashtable. If successful, data contains a pointer to the data
  78. * removed. It is up to the caller to further manage this data.
  79. *
  80. * 0 if removing the element was successful, -1 otherwise
  81. */
  82. int ht_remove(hashtable* ht, void** data) {
  83. list_element *element, *prev;
  84. int bucket;
  85. // Hash the key, then search for the data in the given bucket
  86. bucket = ht->h(*data) % ht->buckets;
  87. prev = 0;
  88. for (element = list_head(&ht->table[bucket]); element != 0; element = list_next(element)) {
  89. if (ht->match(*data, list_data(element))) {
  90. // Found it, remove it
  91. if (list_remove_next(&ht->table[bucket], prev, data) == 0) {
  92. ht->size--;
  93. return 0;
  94. }
  95. }
  96. prev = element;
  97. }
  98. // Data not found
  99. return -1;
  100. }
  101. /**
  102. * Determines whether an element matches the given data in the hashtable. If so, data points to
  103. * the matched value in the hashtable
  104. *
  105. * Returns 0 if a match was found in the hashtable, -1 otherwise
  106. */
  107. int ht_lookup(hashtable* ht, void** data) {
  108. list_element *element;
  109. int bucket;
  110. // Hash the key, then search for the data in the bucket
  111. bucket = ht->h(*data) % ht->buckets;
  112. for (element = list_head(&ht->table[bucket]); element != 0; element = list_next(element)) {
  113. if (ht->match(*data, list_data(element))) {
  114. // We found it
  115. *data = list_data(element);
  116. return 0;
  117. }
  118. }
  119. // Data not found
  120. return -1;
  121. }
  122. /**
  123. * Begin iterating through the hashtable
  124. *
  125. * Returns an iterator representing the first item in the table, or NULL if the table is empty
  126. */
  127. hashtable_iter* ht_iter_begin(hashtable* ht) {
  128. hashtable_iter_impl* hi;
  129. list_element* elem;
  130. int i;
  131. hi = 0;
  132. elem = 0;
  133. for (i = 0; i < ht->buckets; i++) {
  134. elem = list_head(&ht->table[i]);
  135. if (elem) {
  136. break;
  137. }
  138. }
  139. if (elem) {
  140. hi = (hashtable_iter_impl*)malloc(sizeof(hashtable_iter_impl));
  141. hi->ht = ht;
  142. hi->current_bucket = i;
  143. hi->current = elem;
  144. }
  145. return hi;
  146. }
  147. /**
  148. * Given the current iterator, return an iterator representing the next item in the hashtable
  149. * NOTE: Do NOT assume that the iterator passed in to "current" is still valid after this call!
  150. * Also, items in the hashtable are NOT guaranteed to be iterated in any particular order
  151. *
  152. * Returns NULL if we have reached the end of the list
  153. */
  154. hashtable_iter* ht_iter_next(hashtable_iter* current) {
  155. hashtable_iter_impl* cur;
  156. list_element* elem;
  157. int i;
  158. if (!current) {
  159. return 0;
  160. }
  161. cur = (hashtable_iter_impl*)current;
  162. i = cur->current_bucket;
  163. elem = cur->current->next;
  164. if (!elem) {
  165. for (i = cur->current_bucket+1; i < cur->ht->buckets; i++) {
  166. elem = list_head(&cur->ht->table[i]);
  167. if (elem) {
  168. break;
  169. }
  170. }
  171. }
  172. if (elem) {
  173. cur->current_bucket = i;
  174. cur->current = elem;
  175. } else {
  176. free(cur);
  177. cur = 0;
  178. }
  179. return cur;
  180. }
  181. /**
  182. * Returns the value of the given iterator
  183. */
  184. void* ht_value(hashtable_iter* iter) {
  185. return ((hashtable_iter_impl*)iter)->current->data;
  186. }
  187. /**
  188. * The default hashing function - works great for strings. This was taken from the venerable
  189. * Dragon Book and Algorithms With C and was created by P.J. Weinberger
  190. *
  191. */
  192. int ht_hashpjw(const void* key) {
  193. const char* ptr;
  194. unsigned int val;
  195. // Hash the key by performing some bit manipulation on it
  196. val = 0;
  197. ptr = key;
  198. while (*ptr != '\0') {
  199. unsigned int tmp;
  200. val = (val << 4) + (*ptr);
  201. if (tmp = (val & 0xf0000000)) {
  202. val = val ^ (tmp >> 24);
  203. val = val ^ tmp;
  204. }
  205. ptr++;
  206. }
  207. return val;
  208. }
  209. /**
  210. * The default compare function. Just a strcmp
  211. */
  212. int matchstr(const void* key1, const void* key2) {
  213. return !strcmp((const char*)key1, (const char*)key2);
  214. }