hash.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /*
  2. * Copyright (c) 2007,2008 Paulo Cesar Pereira de Andrade
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice (including the next
  12. * paragraph) shall be included in all copies or substantial portions of the
  13. * Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. * DEALINGS IN THE SOFTWARE.
  22. *
  23. * Author: Paulo Cesar Pereira de Andrade
  24. */
  25. #include "util.h"
  26. #include <stdlib.h>
  27. #include <string.h>
  28. /*
  29. * This is a very simplified and adapted version of the hash tables I am
  30. * using in a personal project. It was added to try to have a single hash
  31. * table implementation in xedit. The lisp (for user code) version was not
  32. * converted, but the following hastables have been converted:
  33. * ispell.c - list of replace and ignore words
  34. * hook.c - list of auto replace words
  35. * internal lisp data structures:
  36. * atoms
  37. * strings
  38. * packages
  39. * opaque types
  40. * also, all code traversing hash tables is now using
  41. * hash_iter_first() and hash_iter_next()
  42. * conversion isn't as good as I originally wanted, code is using hash_check
  43. * instead of hash_get, but this is due to the code not having a basic
  44. * { void *data; int length; } object to store string like objects
  45. *
  46. * Also, this hash table implementation was added mainly for the tags
  47. * support.
  48. */
  49. /*
  50. * Prototypes
  51. */
  52. static int hash_equal(hash_table *hash, hash_key *left, hash_key *right);
  53. static unsigned int hash_value(hash_key *key);
  54. /*
  55. * Implementation
  56. */
  57. static int
  58. hash_equal(hash_table *hash, hash_key *left, hash_key *right)
  59. {
  60. if (left->length == right->length) {
  61. if (left == right)
  62. return (1);
  63. if (hash->compare)
  64. return (hash->compare(left, right));
  65. return (memcmp(left->value, right->value, left->length) == 0);
  66. }
  67. return (0);
  68. }
  69. static unsigned int
  70. hash_data(const char *value, unsigned int length)
  71. {
  72. const char *ptr;
  73. unsigned int i, key;
  74. for (i = key = 0, ptr = value; i < length; i++)
  75. key = (key << (key & 1)) ^ ptr[i];
  76. return (key);
  77. }
  78. static unsigned int
  79. hash_value(hash_key *key)
  80. {
  81. return (hash_data(key->value, key->length));
  82. }
  83. hash_table *
  84. hash_new(unsigned int length, hash_compare compare)
  85. {
  86. hash_table *hash;
  87. hash = calloc(1, sizeof(hash_table));
  88. if (hash) {
  89. hash->entries = calloc(length, sizeof(hash_entry *));
  90. if (hash->entries) {
  91. hash->length = length;
  92. hash->compare = compare;
  93. hash->iter.offset = -1;
  94. }
  95. else {
  96. free(hash);
  97. hash = (hash_table *)0;
  98. }
  99. }
  100. return (hash);
  101. }
  102. hash_entry *
  103. hash_put(hash_table *hash, hash_entry *entry)
  104. {
  105. unsigned int key;
  106. hash_entry *prev, *ptr;
  107. /* Offset in hash table vector for this entry */
  108. key = hash_value(entry->key) % hash->length;
  109. /* We hope this is nil for most calls */
  110. ptr = hash->entries[key];
  111. /* Check if clashed with another entry */
  112. for (prev = ptr; ptr; prev = ptr, ptr = ptr->next) {
  113. /* Replace current entry */
  114. if (hash_equal(hash, entry->key, ptr->key)) {
  115. /* If not trying to readd same value */
  116. if (entry != ptr) {
  117. if (ptr == prev)
  118. hash->entries[key] = entry;
  119. else
  120. prev->next = entry;
  121. entry->next = ptr->next;
  122. /* Finished */
  123. }
  124. else
  125. ptr = (hash_entry *)0;
  126. goto hash_put_done;
  127. }
  128. }
  129. /* Add new entry */
  130. if (prev == (hash_entry *)0)
  131. /* If no entry in offset */
  132. hash->entries[key] = entry;
  133. else
  134. /* Add to end of clashing list */
  135. prev->next = entry;
  136. entry->next = (hash_entry *)0;
  137. /* Increase sum of entries counter*/
  138. ++hash->count;
  139. hash_put_done:
  140. /* ptr will be nil if no entry was replaced, of tried to add
  141. * again an entry already in the hash table */
  142. return (ptr);
  143. }
  144. hash_entry *
  145. hash_get(hash_table *hash, hash_key *name)
  146. {
  147. unsigned int key;
  148. hash_entry *entry;
  149. key = hash_value(name) % hash->length;
  150. for (entry = hash->entries[key]; entry; entry = entry->next) {
  151. if (hash_equal(hash, name, entry->key)) {
  152. return (entry);
  153. }
  154. }
  155. return ((hash_entry *)0);
  156. }
  157. hash_entry *
  158. hash_check(hash_table *hash, const char *name, unsigned int length)
  159. {
  160. unsigned int key;
  161. hash_entry *entry;
  162. key = hash_data(name, length) % hash->length;
  163. for (entry = hash->entries[key]; entry; entry = entry->next) {
  164. if (length == entry->key->length &&
  165. memcmp(name, entry->key->value, length) == 0) {
  166. return (entry);
  167. }
  168. }
  169. return ((hash_entry *)0);
  170. }
  171. hash_entry *
  172. hash_rem_no_free(hash_table *hash, hash_entry *entry)
  173. {
  174. unsigned int key;
  175. hash_entry *ptr, *prev;
  176. key = hash_value(entry->key) % hash->length;
  177. for (ptr = prev = hash->entries[key]; ptr; prev = ptr, ptr = ptr->next) {
  178. if (ptr == entry) {
  179. --hash->count;
  180. if (ptr == prev)
  181. hash->entries[key] = ptr->next;
  182. else
  183. prev->next = ptr->next;
  184. break;
  185. }
  186. }
  187. if (ptr && ptr == hash->iter.entry)
  188. hash->iter.entry = ptr->next;
  189. /* If entry wasn't in hash table ptr will be nil */
  190. return (ptr);
  191. }
  192. void
  193. hash_rem(hash_table *hash, hash_entry *entry)
  194. {
  195. entry = hash_rem_no_free(hash, entry);
  196. if (entry) {
  197. free(entry->key->value);
  198. free(entry->key);
  199. free(entry);
  200. }
  201. }
  202. void
  203. hash_rehash(hash_table *hash, unsigned int length)
  204. {
  205. unsigned int i, key;
  206. hash_entry *entry, *next, **entries;
  207. entries = (hash_entry **)calloc(length, sizeof(hash_entry *));
  208. if (entries) {
  209. /* Populate the new table, note that clashes are now in reverse order */
  210. for (i = 0; i < hash->length; i++) {
  211. for (entry = hash->entries[i]; entry; entry = next) {
  212. next = entry->next;
  213. key = hash_value(entry->key) % length;
  214. entry->next = entries[key];
  215. entries[key] = entry;
  216. }
  217. }
  218. /* Finish updating hash table */
  219. free(hash->entries);
  220. hash->entries = entries;
  221. hash->length = length;
  222. }
  223. hash->iter.offset = -1;
  224. }
  225. hash_entry *
  226. hash_iter_first(hash_table *hash)
  227. {
  228. hash->iter.offset = 0;
  229. hash->iter.entry = (hash_entry *)0;
  230. return (hash_iter_next(hash));
  231. }
  232. hash_entry *
  233. hash_iter_next(hash_table *hash)
  234. {
  235. if (hash->iter.offset >= 0) {
  236. if (hash->iter.entry) {
  237. if ((hash->iter.entry = hash->iter.entry->next))
  238. return (hash->iter.entry);
  239. ++hash->iter.offset;
  240. }
  241. for (; hash->iter.offset < hash->length; hash->iter.offset++) {
  242. if ((hash->iter.entry = hash->entries[hash->iter.offset]))
  243. return (hash->iter.entry);
  244. }
  245. hash->iter.entry = (hash_entry *)0;
  246. hash->iter.offset = -1;
  247. }
  248. return ((hash_entry *)0);
  249. }
  250. void
  251. hash_clr(hash_table *hash)
  252. {
  253. unsigned int i;
  254. hash_entry *entry, *next;
  255. /* Extra data should be free'd with the iterator */
  256. for (i = 0; i < hash->length; i++) {
  257. entry = hash->entries[i];
  258. if (entry) {
  259. for (next = entry; entry; entry = next) {
  260. next = entry->next;
  261. free(entry->key->value);
  262. free(entry->key);
  263. free(entry);
  264. }
  265. hash->entries[i] = (hash_entry *)0;
  266. }
  267. }
  268. hash->count = 0;
  269. hash->iter.offset = -1;
  270. }
  271. void
  272. hash_del(hash_table *hash)
  273. {
  274. hash_clr(hash);
  275. free(hash->entries);
  276. free(hash);
  277. }