Separate netbsd-specific from unix-specific projects
[eleutheria.git] / netbsd / genstructs / htable / htable.c
blob63e5c265e79956c22c0a6ba8058852d3cdb0e393
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/queue.h>
5 #include "htable.h"
7 htret_t htable_init(htable_t *htable, size_t size, size_t factor,
8 hashf_t *myhashf,
9 cmpf_t *mycmpf,
10 printf_t *myprintf)
12 size_t i;
14 /* Allocate memory for `size' tailq headers */
15 if ((htable->ht_table = malloc(size * sizeof *htable->ht_table)) == NULL)
16 return HT_NOMEM;
18 /* Initialize tailqs */
19 for (i = 0; i < size; i++)
20 TAILQ_INIT(&htable->ht_table[i]);
22 htable->ht_size = size; /* size must be a power of 2 */
23 htable->ht_used = 0;
24 htable->ht_factor = factor;
25 htable->ht_limit = factor * size;
26 #ifdef HTABLE_STATS
27 htable->ht_grows = 0;
28 #endif
30 /* Setup callback functions */
31 htable->ht_hashf = myhashf;
32 htable->ht_cmpf = mycmpf;
33 htable->ht_printf = myprintf;
35 return HT_OK;
38 void htable_free(htable_t *htable)
40 hhead_t *phead;
41 hnode_t *pnode;
42 size_t i;
44 for (i = 0; i < htable->ht_size; i++) {
45 phead = &htable->ht_table[i];
46 while ((pnode = TAILQ_FIRST(phead)) != NULL) {
47 TAILQ_REMOVE(phead, pnode, hn_next);
48 free(pnode);
52 free(htable->ht_table);
55 htret_t htable_free_obj(htable_t *htable, void *key, htfree_t htfree)
57 hhead_t *phead;
58 hnode_t *pnode;
59 size_t hash;
61 /* Calculate hash */
62 hash = htable->ht_hashf(key);
65 * Search across chain if there is an entry with the
66 * key we are looking for. If there is, free its contents.
68 phead = &htable->ht_table[hash & (htable->ht_size - 1)];
69 TAILQ_FOREACH(pnode, phead, hn_next) {
70 if (htable->ht_cmpf(pnode->hn_key, key) == 0) {
71 TAILQ_REMOVE(phead, pnode, hn_next);
72 if (htfree & HT_FREEKEY)
73 free(pnode->hn_key);
74 if (htfree & HT_FREEDATA)
75 free(pnode->hn_data);
76 free(pnode);
77 htable->ht_used--;
78 return HT_OK;
82 return HT_NOTFOUND;
85 void htable_free_all_obj(htable_t *htable, htfree_t htfree)
87 hhead_t *phead;
88 hnode_t *pnode;
89 size_t i;
91 for (i = 0; i < htable->ht_size; i++) {
92 phead = &htable->ht_table[i];
93 TAILQ_FOREACH(pnode, phead, hn_next) {
94 if (htfree & HT_FREEKEY)
95 free(pnode->hn_key);
96 if (htfree & HT_FREEDATA)
97 free(pnode->hn_data);
102 htret_t htable_grow(htable_t *htable)
104 hhead_t *pcurhead, *pnewhead, *poldhead;
105 hnode_t *pnode;
106 size_t i, newhash, newsize;
109 * Allocate memory for new hash table
110 * The new hash table is 2 times bigger than the old one
112 newsize = htable->ht_size << 1;
113 if ((pnewhead = malloc(newsize * sizeof *pnewhead)) == NULL)
114 return HT_NOMEM;
116 /* Initialize tailqs */
117 for (i = 0; i < newsize; i++)
118 TAILQ_INIT(&pnewhead[i]);
120 poldhead = htable->ht_table;
123 * Remove the entries from the old hash table,
124 * rehash them in respect to the new hash table,
125 * and add them to the new one
127 for (i = 0; i < htable->ht_size; i++) {
128 pcurhead = &poldhead[i];
129 while ((pnode = TAILQ_FIRST(pcurhead)) != NULL) {
130 newhash = htable->ht_hashf(pnode->hn_key);
131 TAILQ_REMOVE(pcurhead, pnode, hn_next);
132 TAILQ_INSERT_TAIL(&pnewhead[newhash & (newsize - 1)], pnode, hn_next);
136 /* Free old table */
137 free(htable->ht_table);
139 /* Set new table parameters */
140 htable->ht_table = pnewhead;
141 htable->ht_size = newsize;
142 htable->ht_limit = htable->ht_factor * newsize;
144 return HT_OK;
147 htret_t htable_insert(htable_t *htable, void *key, void *data)
149 hhead_t *phead;
150 hnode_t *pnode;
151 size_t hash;
153 /* Calculate hash */
154 hash = htable->ht_hashf(key);
156 /* Search across chain if there is already an entry with the same key. */
157 phead = &htable->ht_table[hash & (htable->ht_size - 1)];
158 TAILQ_FOREACH(pnode, phead, hn_next)
159 if (htable->ht_cmpf(pnode->hn_key, key) == 0)
160 return HT_EXISTS;
162 /* Allocate memory for new entry */
163 if ((pnode = malloc(sizeof *pnode)) == NULL)
164 return HT_NOMEM;
165 pnode->hn_key = key;
166 pnode->hn_data = data;
168 TAILQ_INSERT_TAIL(phead, pnode, hn_next);
170 /* If used items exceed limit, grow the table */
171 if (++htable->ht_used > htable->ht_limit) {
172 htable_grow(htable);
173 #ifdef HTABLE_STATS
174 htable->ht_grows++;
175 #endif
178 return HT_OK;
181 htret_t htable_remove(htable_t *htable, const void *key)
183 hhead_t *phead;
184 hnode_t *pnode;
185 size_t hash;
187 /* Calculate hash */
188 hash = htable->ht_hashf(key);
191 * Search across chain if there is an entry with the
192 * key we are looking. If there is, delete it.
194 phead = &htable->ht_table[hash & (htable->ht_size - 1)];
195 TAILQ_FOREACH(pnode, phead, hn_next) {
196 if (htable->ht_cmpf(pnode->hn_key, key) == 0) {
197 TAILQ_REMOVE(phead, pnode, hn_next);
198 free(pnode);
199 htable->ht_used--;
200 return HT_OK;
204 return HT_NOTFOUND;
207 void *htable_search(const htable_t *htable, const void *key)
209 const hhead_t *phead;
210 const hnode_t *pnode;
211 size_t hash;
213 /* Calculate hash */
214 hash = htable->ht_hashf(key);
216 phead = &htable->ht_table[hash & (htable->ht_size - 1)];
217 TAILQ_FOREACH(pnode, phead, hn_next)
218 if (htable->ht_cmpf(pnode->hn_key, key) == 0)
219 return pnode->hn_data;
221 return NULL;
224 void htable_print(const htable_t *htable)
226 const hhead_t *phead;
227 const hnode_t *pnode;
228 size_t i;
230 for (i = 0; i < htable->ht_size; i++) {
231 phead = &htable->ht_table[i];
232 TAILQ_FOREACH(pnode, phead, hn_next)
233 htable->ht_printf(pnode->hn_key, pnode->hn_data);
234 if (TAILQ_FIRST(&htable->ht_table[i]) != NULL)
235 printf("\n");
239 size_t htable_get_size(const htable_t *htable)
241 return htable->ht_size;
244 size_t htable_get_used(const htable_t *htable)
246 return htable->ht_used;
249 void htable_traverse(const htable_t *htable, void (*pfunc)(void *data))
251 const hhead_t *phead;
252 const hnode_t *pnode;
253 size_t i;
255 for (i = 0; i < htable->ht_size; i++) {
256 phead = &htable->ht_table[i];
257 TAILQ_FOREACH(pnode, phead, hn_next)
258 pfunc(pnode->hn_data);
262 const hnode_t *htable_get_next_elm(const htable_t *htable, size_t *pos, const hnode_t *pnode)
264 const hhead_t *phead;
265 size_t i;
267 /* Is pos out of bound ? If yes, return immediately */
268 if (*pos > (htable->ht_size - 1))
269 return NULL;
271 /* Find first usable element, if we were not supplied with one */
272 if (pos == 0 || pnode == NULL) {
273 for (i = *pos; i < htable->ht_size; i++) {
274 ++*pos;
275 phead = &htable->ht_table[i];
276 if (TAILQ_FIRST(phead) != NULL)
277 return TAILQ_FIRST(phead);
279 /* We have traversed all elements. Nothing left. */
280 return NULL;
283 /* Are we on a chain ? */
284 if (TAILQ_NEXT(pnode, hn_next) != NULL) {
286 * Don't increase pos since we are stack in an horizontal chain,
287 * being still at the same 'height' which is what pos represents anyway
289 return TAILQ_NEXT(pnode, hn_next);
291 else {
292 for (i = *pos; i < htable->ht_size; i++) {
293 ++*pos;
294 phead = &htable->ht_table[i];
295 if (TAILQ_FIRST(phead) != NULL)
296 return TAILQ_FIRST(phead);
298 /* We have traversed all elements. Nothing left. */
299 return NULL;
303 #ifdef HTABLE_STATS
304 size_t htable_stat_get_grows(const htable_t *htable)
306 return htable->ht_grows;
309 size_t htable_stat_get_chain_len(const htable_t *htable, size_t pos)
311 const hhead_t *phead;
312 const hnode_t *pnode;
313 size_t len;
315 if (pos >= htable->ht_size)
316 return 0; /* FIXME: Better error handling */
318 len = 0;
319 phead = &htable->ht_table[pos];
320 TAILQ_FOREACH(pnode, phead, hn_next)
321 len++;
323 return len;
325 #endif