hashtbl.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /* ----------------------------------------------------------------------- *
  2. *
  3. * Copyright 1996-2009 The NASM Authors - All Rights Reserved
  4. * See the file AUTHORS included with the NASM distribution for
  5. * the specific copyright holders.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following
  9. * conditions are met:
  10. *
  11. * * Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * * Redistributions in binary form must reproduce the above
  14. * copyright notice, this list of conditions and the following
  15. * disclaimer in the documentation and/or other materials provided
  16. * with the distribution.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  19. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  20. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  25. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  26. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  28. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  29. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  30. * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. * ----------------------------------------------------------------------- */
  33. /*
  34. * hashtbl.c
  35. *
  36. * Efficient dictionary hash table class.
  37. */
  38. #include "compiler.h"
  39. #include <string.h>
  40. #include "nasm.h"
  41. #include "hashtbl.h"
  42. #define HASH_MAX_LOAD 2 /* Higher = more memory-efficient, slower */
  43. #define hash_calc(key) crc64(CRC64_INIT, (key))
  44. #define hash_calci(key) crc64i(CRC64_INIT, (key))
  45. #define hash_max_load(size) ((size) * (HASH_MAX_LOAD - 1) / HASH_MAX_LOAD)
  46. #define hash_expand(size) ((size) << 1)
  47. #define hash_mask(size) ((size) - 1)
  48. #define hash_pos(hash, mask) ((hash) & (mask))
  49. #define hash_inc(hash, mask) ((((hash) >> 32) & (mask)) | 1) /* always odd */
  50. #define hash_pos_next(pos, inc, mask) (((pos) + (inc)) & (mask))
  51. static struct hash_tbl_node *alloc_table(size_t newsize)
  52. {
  53. size_t bytes = newsize * sizeof(struct hash_tbl_node);
  54. return nasm_zalloc(bytes);
  55. }
  56. void hash_init(struct hash_table *head, size_t size)
  57. {
  58. nasm_assert(is_power2(size));
  59. head->table = alloc_table(size);
  60. head->load = 0;
  61. head->size = size;
  62. head->max_load = hash_max_load(size);
  63. }
  64. /*
  65. * Find an entry in a hash table.
  66. *
  67. * On failure, if "insert" is non-NULL, store data in that structure
  68. * which can be used to insert that node using hash_add().
  69. *
  70. * WARNING: this data is only valid until the very next call of
  71. * hash_add(); it cannot be "saved" to a later date.
  72. *
  73. * On success, return a pointer to the "data" element of the hash
  74. * structure.
  75. */
  76. void **hash_find(struct hash_table *head, const char *key,
  77. struct hash_insert *insert)
  78. {
  79. struct hash_tbl_node *np;
  80. struct hash_tbl_node *tbl = head->table;
  81. uint64_t hash = hash_calc(key);
  82. size_t mask = hash_mask(head->size);
  83. size_t pos = hash_pos(hash, mask);
  84. size_t inc = hash_inc(hash, mask);
  85. while ((np = &tbl[pos])->key) {
  86. if (hash == np->hash && !strcmp(key, np->key))
  87. return &np->data;
  88. pos = hash_pos_next(pos, inc, mask);
  89. }
  90. /* Not found. Store info for insert if requested. */
  91. if (insert) {
  92. insert->head = head;
  93. insert->hash = hash;
  94. insert->where = np;
  95. }
  96. return NULL;
  97. }
  98. /*
  99. * Same as hash_find, but for case-insensitive hashing.
  100. */
  101. void **hash_findi(struct hash_table *head, const char *key,
  102. struct hash_insert *insert)
  103. {
  104. struct hash_tbl_node *np;
  105. struct hash_tbl_node *tbl = head->table;
  106. uint64_t hash = hash_calci(key);
  107. size_t mask = hash_mask(head->size);
  108. size_t pos = hash_pos(hash, mask);
  109. size_t inc = hash_inc(hash, mask);
  110. while ((np = &tbl[pos])->key) {
  111. if (hash == np->hash && !nasm_stricmp(key, np->key))
  112. return &np->data;
  113. pos = hash_pos_next(pos, inc, mask);
  114. }
  115. /* Not found. Store info for insert if requested. */
  116. if (insert) {
  117. insert->head = head;
  118. insert->hash = hash;
  119. insert->where = np;
  120. }
  121. return NULL;
  122. }
  123. /*
  124. * Insert node. Return a pointer to the "data" element of the newly
  125. * created hash node.
  126. */
  127. void **hash_add(struct hash_insert *insert, const char *key, void *data)
  128. {
  129. struct hash_table *head = insert->head;
  130. struct hash_tbl_node *np = insert->where;
  131. /*
  132. * Insert node. We can always do this, even if we need to
  133. * rebalance immediately after.
  134. */
  135. np->hash = insert->hash;
  136. np->key = key;
  137. np->data = data;
  138. if (++head->load > head->max_load) {
  139. /* Need to expand the table */
  140. size_t newsize = hash_expand(head->size);
  141. struct hash_tbl_node *newtbl = alloc_table(newsize);
  142. size_t mask = hash_mask(newsize);
  143. if (head->table) {
  144. struct hash_tbl_node *op, *xp;
  145. size_t i;
  146. /* Rebalance all the entries */
  147. for (i = 0, op = head->table; i < head->size; i++, op++) {
  148. if (op->key) {
  149. size_t pos = hash_pos(op->hash, mask);
  150. size_t inc = hash_inc(op->hash, mask);
  151. while ((xp = &newtbl[pos])->key)
  152. pos = hash_pos_next(pos, inc, mask);
  153. *xp = *op;
  154. if (op == np)
  155. np = xp;
  156. }
  157. }
  158. nasm_free(head->table);
  159. }
  160. head->table = newtbl;
  161. head->size = newsize;
  162. head->max_load = hash_max_load(newsize);
  163. }
  164. return &np->data;
  165. }
  166. /*
  167. * Iterate over all members of a hash set. For the first call,
  168. * iterator should be initialized to NULL. Returns the data pointer,
  169. * or NULL on failure.
  170. */
  171. void *hash_iterate(const struct hash_table *head,
  172. struct hash_tbl_node **iterator,
  173. const char **key)
  174. {
  175. struct hash_tbl_node *np = *iterator;
  176. struct hash_tbl_node *ep = head->table + head->size;
  177. if (!np) {
  178. np = head->table;
  179. if (!np)
  180. return NULL; /* Uninitialized table */
  181. }
  182. while (np < ep) {
  183. if (np->key) {
  184. *iterator = np + 1;
  185. if (key)
  186. *key = np->key;
  187. return np->data;
  188. }
  189. np++;
  190. }
  191. *iterator = NULL;
  192. if (key)
  193. *key = NULL;
  194. return NULL;
  195. }
  196. /*
  197. * Free the hash itself. Doesn't free the data elements; use
  198. * hash_iterate() to do that first, if needed. This function is normally
  199. * used when the hash data entries are either freed separately, or
  200. * compound objects which can't be freed in a single operation.
  201. */
  202. void hash_free(struct hash_table *head)
  203. {
  204. void *p = head->table;
  205. head->table = NULL;
  206. nasm_free(p);
  207. }
  208. /*
  209. * Frees the hash *and* all data elements. This is applicable only in
  210. * the case where the data element is a single allocation. If the
  211. * second argument is false, the key string is part of the data
  212. * allocation or belongs to an allocation which will be freed
  213. * separately, if it is true the keys are also freed.
  214. */
  215. void hash_free_all(struct hash_table *head, bool free_keys)
  216. {
  217. struct hash_tbl_node *iter = NULL;
  218. const char *keyp;
  219. void *d;
  220. while ((d = hash_iterate(head, &iter, &keyp))) {
  221. nasm_free(d);
  222. if (free_keys)
  223. nasm_free((void *)keyp);
  224. }
  225. hash_free(head);
  226. }