hashtable.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. //*****************************************************************************
  2. //
  3. // hashtable.c
  4. //
  5. // Your friendly neighbourhood hashtable. Maps a <key> to a <value>. *sigh*
  6. // why am I not writing this MUD in C++, again?
  7. //
  8. //*****************************************************************************
  9. #include <stdlib.h>
  10. #include <ctype.h>
  11. #include <string.h>
  12. #include "list.h"
  13. #include "hashtable.h"
  14. #include "mud.h"
  15. #include "utils.h"
  16. // how big of a size do our hashtables start out at?
  17. #define DEFAULT_HASH_SIZE 5
  18. struct hashtable_iterator {
  19. unsigned int curr_bucket;
  20. HASHTABLE *table;
  21. LIST_ITERATOR *bucket_i;
  22. };
  23. typedef struct hashtable_entry {
  24. char *key;
  25. void *val;
  26. } HASH_ENTRY;
  27. struct hashtable {
  28. int size;
  29. int num_buckets;
  30. LIST **buckets;
  31. };
  32. //
  33. // an internal form of hashGet that returns the entire entry (key and val)
  34. HASH_ENTRY *hashGetEntry(HASHTABLE *table, const char *key){
  35. unsigned int bucket = string_hash(key) % table->num_buckets;
  36. if(table->buckets[bucket] == NULL)
  37. return NULL;
  38. else {
  39. LIST_ITERATOR *list_i = newListIterator(table->buckets[bucket]);
  40. HASH_ENTRY *elem = NULL;
  41. for(;(elem = listIteratorCurrent(list_i)) != NULL; listIteratorNext(list_i))
  42. if(!strcasecmp(key, elem->key))
  43. break;
  44. deleteListIterator(list_i);
  45. return elem;
  46. }
  47. }
  48. HASH_ENTRY *newHashtableEntry(const char *key, void *val) {
  49. HASH_ENTRY *entry = malloc(sizeof(HASH_ENTRY));
  50. entry->key = strdup(key);
  51. entry->val = val;
  52. return entry;
  53. }
  54. void deleteHashtableEntry(HASH_ENTRY *entry) {
  55. if(entry->key) free(entry->key);
  56. free(entry);
  57. }
  58. //
  59. // Collect all of the HASH_ENTRYs in a hashtable into a single list
  60. LIST *hashCollectEntries(HASHTABLE *table) {
  61. LIST *list = newList();
  62. int i;
  63. for(i = 0; i < table->num_buckets; i++) {
  64. if(table->buckets[i] == NULL) continue;
  65. LIST_ITERATOR *list_i = newListIterator(table->buckets[i]);
  66. HASH_ENTRY *elem = NULL;
  67. for(;(elem=listIteratorCurrent(list_i)) != NULL;listIteratorNext(list_i))
  68. listPut(list, elem);
  69. deleteListIterator(list_i);
  70. }
  71. return list;
  72. }
  73. //*****************************************************************************
  74. // implementation of hashtable.h
  75. // documentation in hashtable.h
  76. //*****************************************************************************
  77. HASHTABLE *newHashtableSize(int num_buckets) {
  78. int i;
  79. HASHTABLE *table = malloc(sizeof(HASHTABLE));
  80. table->num_buckets = num_buckets;
  81. table->size = 0;
  82. table->buckets = malloc(sizeof(LIST *) * num_buckets);
  83. for(i = 0; i < num_buckets; i++)
  84. table->buckets[i] = NULL;
  85. return table;
  86. }
  87. HASHTABLE *newHashtable(void) {
  88. return newHashtableSize(DEFAULT_HASH_SIZE);
  89. }
  90. void deleteHashtable(HASHTABLE *table) {
  91. int i;
  92. for(i = 0; i < table->num_buckets; i++) {
  93. if(table->buckets[i])
  94. deleteListWith(table->buckets[i], deleteHashtableEntry);
  95. }
  96. free(table->buckets);
  97. free(table);
  98. }
  99. void deleteHashtableWith(HASHTABLE *table, void *func) {
  100. hashClearWith(table, func);
  101. deleteHashtable(table);
  102. }
  103. //
  104. // expand a hashtable to the new size
  105. void hashExpand(HASHTABLE *table, int size) {
  106. // collect all of the key:value pairs
  107. LIST *entries = hashCollectEntries(table);
  108. HASH_ENTRY *entry = NULL;
  109. int i;
  110. // delete all of the current buckets
  111. for(i = 0; i < table->num_buckets; i++) {
  112. if(table->buckets[i] == NULL) continue;
  113. deleteList(table->buckets[i]);
  114. }
  115. free(table->buckets);
  116. // now, make new buckets and set them to NULL
  117. table->buckets = calloc(size, sizeof(LIST *));
  118. table->num_buckets = size;
  119. // now, we put all of our entries back into the new buckets
  120. while((entry = listPop(entries)) != NULL) {
  121. unsigned int bucket = string_hash(entry->key) % table->num_buckets;
  122. if(table->buckets[bucket] == NULL) table->buckets[bucket] = newList();
  123. listPut(table->buckets[bucket], entry);
  124. }
  125. deleteList(entries);
  126. }
  127. int hashPut(HASHTABLE *table, const char *key, void *val) {
  128. HASH_ENTRY *elem = hashGetEntry(table, key);
  129. // if it's already in, update the value
  130. if(elem) {
  131. elem->val = val;
  132. return 1;
  133. }
  134. else {
  135. // first, see if we'll need to expand the table
  136. if((table->size * 80)/100 > table->num_buckets)
  137. hashExpand(table, (table->num_buckets * 150)/100);
  138. unsigned int bucket = string_hash(key) % table->num_buckets;
  139. // if the bucket doesn't exist yet, create it
  140. if(table->buckets[bucket] == NULL)
  141. table->buckets[bucket] = newList();
  142. HASH_ENTRY *entry = newHashtableEntry(key, val);
  143. listPut(table->buckets[bucket], entry);
  144. table->size++;
  145. return 1;
  146. }
  147. }
  148. void *hashGet(HASHTABLE *table, const char *key) {
  149. HASH_ENTRY *elem = hashGetEntry(table, key);
  150. if(elem != NULL)
  151. return elem->val;
  152. else
  153. return NULL;
  154. }
  155. void *hashRemove(HASHTABLE *table, const char *key) {
  156. unsigned int bucket = string_hash(key) % table->num_buckets;
  157. if(table->buckets[bucket] == NULL)
  158. return NULL;
  159. else {
  160. LIST_ITERATOR *list_i = newListIterator(table->buckets[bucket]);
  161. HASH_ENTRY *elem = NULL;
  162. for(;(elem = listIteratorCurrent(list_i)) != NULL; listIteratorNext(list_i))
  163. if(!strcasecmp(key, elem->key))
  164. break;
  165. deleteListIterator(list_i);
  166. if(elem) {
  167. void *val = elem->val;
  168. listRemove(table->buckets[bucket], elem);
  169. deleteHashtableEntry(elem);
  170. table->size--;
  171. return val;
  172. }
  173. else
  174. return NULL;
  175. }
  176. }
  177. int hashIn (HASHTABLE *table, const char *key) {
  178. return (hashGetEntry(table, key) != NULL);
  179. }
  180. int hashSize (HASHTABLE *table) {
  181. return table->size;
  182. }
  183. LIST *hashCollect(HASHTABLE *table) {
  184. LIST *list = newList();
  185. int i;
  186. for(i = 0; i < table->num_buckets; i++) {
  187. if(table->buckets[i] == NULL) continue;
  188. LIST_ITERATOR *list_i = newListIterator(table->buckets[i]);
  189. HASH_ENTRY *elem = NULL;
  190. for(;(elem=listIteratorCurrent(list_i)) != NULL;listIteratorNext(list_i))
  191. listPut(list, strdup(elem->key));
  192. deleteListIterator(list_i);
  193. }
  194. return list;
  195. }
  196. void hashClear(HASHTABLE *table) {
  197. hashClearWith(table, NULL);
  198. }
  199. void hashClearWith(HASHTABLE *table, void *func) {
  200. void (* free_func)(void *) = func;
  201. int i;
  202. for(i = 0; i < table->num_buckets; i++) {
  203. if(table->buckets[i] == NULL) continue;
  204. HASH_ENTRY *elem = NULL;
  205. while( (elem = listPop(table->buckets[i])) != NULL) {
  206. if(free_func && elem->val)
  207. free_func(elem->val);
  208. deleteHashtableEntry(elem);
  209. }
  210. }
  211. }
  212. //*****************************************************************************
  213. // implementation of the hashtable iterator
  214. // documentation in hashtable.h
  215. //*****************************************************************************
  216. HASH_ITERATOR *newHashIterator(HASHTABLE *table) {
  217. HASH_ITERATOR *I = malloc(sizeof(HASH_ITERATOR));
  218. I->table = table;
  219. I->bucket_i = NULL;
  220. hashIteratorReset(I);
  221. return I;
  222. }
  223. void deleteHashIterator (HASH_ITERATOR *I) {
  224. if(I->bucket_i) deleteListIterator(I->bucket_i);
  225. free(I);
  226. }
  227. void hashIteratorReset (HASH_ITERATOR *I) {
  228. int i;
  229. if(I->bucket_i) deleteListIterator(I->bucket_i);
  230. I->bucket_i = NULL;
  231. I->curr_bucket = 0;
  232. // bucket_i will be NULL if there are no elements
  233. for(i = 0; i < I->table->num_buckets; i++) {
  234. if(I->table->buckets[i] != NULL &&
  235. listSize(I->table->buckets[i]) > 0) {
  236. I->curr_bucket = i;
  237. I->bucket_i = newListIterator(I->table->buckets[i]);
  238. break;
  239. }
  240. }
  241. }
  242. void hashIteratorNext (HASH_ITERATOR *I) {
  243. // no elements in the hashtable
  244. if(I->bucket_i == NULL)
  245. return;
  246. // we're at the end of our list
  247. else if(listIteratorNext(I->bucket_i) == NULL) {
  248. deleteListIterator(I->bucket_i);
  249. I->bucket_i = NULL;
  250. I->curr_bucket++;
  251. for(; I->curr_bucket < I->table->num_buckets; I->curr_bucket++) {
  252. if(I->table->buckets[I->curr_bucket] != NULL &&
  253. listSize(I->table->buckets[I->curr_bucket]) > 0) {
  254. I->bucket_i = newListIterator(I->table->buckets[I->curr_bucket]);
  255. break;
  256. }
  257. }
  258. }
  259. }
  260. const char *hashIteratorCurrentKey (HASH_ITERATOR *I) {
  261. if(!I->bucket_i)
  262. return NULL;
  263. else {
  264. HASH_ENTRY *entry = listIteratorCurrent(I->bucket_i);
  265. if(entry)
  266. return entry->key;
  267. else
  268. return NULL;
  269. }
  270. }
  271. void *hashIteratorCurrentVal (HASH_ITERATOR *I) {
  272. if(!I->bucket_i)
  273. return NULL;
  274. else {
  275. HASH_ENTRY *entry = listIteratorCurrent(I->bucket_i);
  276. if(entry)
  277. return entry->val;
  278. else
  279. return NULL;
  280. }
  281. }