SDDSlib
Loading...
Searching...
No Matches
hashtab.c
Go to the documentation of this file.
1/**
2 * @file hashtab.c
3 * @author Bob Jenkins
4 * @date 1996
5 * @brief Implements a hash table.
6 *
7 * Keys are unique. Adding an item fails if the key is already there.
8 * Keys and items are pointed at, not copied. If you change the value
9 * of the key after it is inserted then `hfind()` will not be able to find it.
10 * The hash table maintains a position that can be set and queried.
11 * The table length doubles dynamically and never shrinks. The insert
12 * that causes table doubling may take a long time.
13 * The table length splits when the table length equals the number of items.
14 * Comparisons usually take 7 instructions.
15 * Computing a hash value takes 35+6n instructions for an n-byte key.
16 *
17 * Functions provided:
18 * - `hcreate()` - create a hash table
19 * - `hdestroy()` - destroy a hash table
20 * - `hcount()` - the number of items in the hash table
21 * - `hkey()` - key at the current position
22 * - `hkeyl()` - key length at the current position
23 * - `hstuff()` - stuff at the current position
24 * - `hfind()` - find an item in the table
25 * - `hadd()` - insert an item into the table
26 * - `hdel()` - delete an item from the table
27 * - `hstat()` - print statistics about the table
28 * - `hfirst()` - position at the first item in the table
29 * - `hnext()` - move the position to the next item in the table
30 */
31
32#ifndef STANDARD
33# include "standard.h"
34#endif
35#ifndef LOOKUPA
36# include "lookupa.h"
37#endif
38#ifndef HASHTAB
39# include "hashtab.h"
40#endif
41#ifndef RECYCLE
42# include "recycle.h"
43#endif
44
45#ifdef HSANITY
46/* sanity check -- make sure ipos, apos, and count make sense */
47static void hsanity(t)
48 htab *t;
49{
50 ub4 i, end, counter;
51 hitem *h;
52
53 /* test that apos makes sense */
54 end = (ub4)1 << (t->logsize);
55 if (end < t->apos)
56 printf("error: end %ld apos %ld\n", end, t->apos);
57
58 /* test that ipos is in bucket apos */
59 if (t->ipos) {
60 for (h = t->table[t->apos]; h && h != t->ipos; h = h->next)
61 ;
62 if (h != t->ipos)
63 printf("error:ipos not in apos, apos is %ld\n", t->apos);
64 }
65
66 /* test that t->count is the number of elements in the table */
67 counter = 0;
68 for (counter = 0, i = 0; i < end; ++i)
69 for (h = t->table[i]; h; h = h->next)
70 ++counter;
71 if (counter != t->count)
72 printf("error: counter %ld t->count %ld\n", counter, t->count);
73}
74#endif
75
76/*
77 * hgrow - Double the size of a hash table.
78 * Allocate a new, 2x bigger array,
79 * move everything from the old array to the new array,
80 * then free the old array.
81 */
82static void hgrow(t)
83 htab *t; /* table */
84{
85 register ub4 newsize = (ub4)1 << (++t->logsize);
86 register ub4 newmask = newsize - 1;
87 register ub4 i;
88 register hitem **oldtab = t->table;
89 register hitem **newtab = (hitem **)malloc(newsize * sizeof(hitem *));
90
91 /* make sure newtab is cleared */
92 for (i = 0; i < newsize; ++i)
93 newtab[i] = (hitem *)0;
94 t->table = newtab;
95 t->mask = newmask;
96
97 /* Walk through old table putting entries in new table */
98 for (i = newsize >> 1; i--;) {
99 register hitem *this, *that, **newplace;
100 for (this = oldtab[i]; this;) {
101 that = this;
102 this = this->next;
103 newplace = &newtab[(that->hval & newmask)];
104 that->next = *newplace;
105 *newplace = that;
106 }
107 }
108
109 /* position the hash table on some existing item */
110 hfirst(t);
111
112 /* free the old array */
113 free((char *)oldtab);
114}
115
116/**
117 * @brief Create a hash table.
118 *
119 * Initializes a hash table with an initial size of 2 raised to the power of `logsize`.
120 *
121 * @param logsize Log base 2 of the initial size of the hash table.
122 * @return Pointer to the newly created hash table.
123 */
124htab *hcreate(logsize)
125 word logsize; /* log base 2 of the size of the hash table */
126{
127 ub4 i, len;
128 htab *t = (htab *)malloc(sizeof(htab));
129
130 len = ((ub4)1 << logsize);
131 t->table = (hitem **)malloc(sizeof(hitem *) * (ub4)len);
132 for (i = 0; i < len; ++i)
133 t->table[i] = (hitem *)0;
134 t->logsize = logsize;
135 t->mask = len - 1;
136 t->count = 0;
137 t->apos = (ub4)0;
138 t->ipos = (hitem *)0;
139 t->space = remkroot(sizeof(hitem));
140 t->bcount = 0;
141 return t;
142}
143
144/**
145 * @brief Destroy the hash table and free all its memory.
146 *
147 * @param t Pointer to the hash table to destroy.
148 */
149void hdestroy(t)
150 htab *t; /* the table */
151{
152 while (hcount(t)) /* while the table is not empty */
153 {
154 if (hkey(t))
155 free(hkey(t)); /* free memory for the line */
156 hdel(t); /* delete from hash table */
157 }
158 refree(t->space);
159 free((char *)t->table);
160 free((char *)t);
161}
162
163/* hcount() is a macro, see hashtab.h */
164/* hkey() is a macro, see hashtab.h */
165/* hkeyl() is a macro, see hashtab.h */
166/* hstuff() is a macro, see hashtab.h */
167
168/**
169 * @brief Find an item with a given key in the hash table.
170 *
171 * Searches for an item with the specified key in the hash table.
172 *
173 * @param t Pointer to the hash table.
174 * @param key Key to find.
175 * @param keyl Length of the key.
176 * @return TRUE if the item is found, FALSE otherwise.
177 */
178word hfind(t, key, keyl)
179 htab *t; /* table */
180ub1 *key; /* key to find */
181ub4 keyl; /* key length */
182{
183 hitem *h;
184 ub4 x = lookup(key, keyl, 0);
185 ub4 y;
186 for (h = t->table[y = (x & t->mask)]; h; h = h->next) {
187 if ((x == h->hval) && (keyl == h->keyl) && !memcmp(key, h->key, keyl)) {
188 t->apos = y;
189 t->ipos = h;
190 return TRUE;
191 }
192 }
193 return FALSE;
194}
195
196/**
197 * @brief Add an item to the hash table.
198 *
199 * Inserts an item into the hash table with the given key and associated data.
200 *
201 * @param t Pointer to the hash table.
202 * @param key Key to add to the hash table.
203 * @param keyl Length of the key.
204 * @param stuff Data to associate with the key.
205 * @return TRUE if the item was added successfully, FALSE if the key is already in the table.
206 */
207word hadd(t, key, keyl, stuff)
208 htab *t; /* table */
209ub1 *key; /* key to add to hash table */
210ub4 keyl; /* key length */
211void *stuff; /* stuff to associate with this key */
212{
213 register hitem *h, **hp;
214 register ub4 y, x = lookup(key, keyl, 0);
215 /* make sure the key is not already there */
216 for (h = t->table[(y = (x & t->mask))]; h; h = h->next) {
217 if ((x == h->hval) && (keyl == h->keyl) && !memcmp(key, h->key, keyl)) {
218 t->apos = y;
219 t->ipos = h;
220 return FALSE;
221 }
222 }
223
224 /* find space for a new item */
225 h = (hitem *)renew(t->space);
226
227 /* make the hash table bigger if it is getting full */
228 if (++t->count > (ub4)1 << (t->logsize)) {
229 hgrow(t);
230 y = (x & t->mask);
231 }
232
233 /* add the new key to the table */
234
235 h->key = (ub1 *)malloc(keyl); /* dumb use of malloc */
236 memcpy(h->key, key, keyl); /* copy key into h->key */
237 h->keyl = keyl;
238 h->stuff = stuff;
239 h->hval = x;
240 hp = &t->table[y];
241 h->next = *hp;
242 *hp = h;
243 t->ipos = h;
244 t->apos = y;
245
246#ifdef HSANITY
247 hsanity(t);
248#endif /* HSANITY */
249
250 return TRUE;
251}
252
253/* hdel - delete the item at the current position */
254word hdel(t)
255 htab *t; /* the hash table */
256{
257 hitem *h; /* item being deleted */
258 hitem **ip; /* a counter */
259
260 /* check for item not existing */
261 if (!(h = t->ipos))
262 return FALSE;
263
264 /* remove item from its list */
265 for (ip = &t->table[t->apos]; *ip != h; ip = &(*ip)->next)
266 ;
267 *ip = (*ip)->next;
268 --(t->count);
269
270 /* adjust position to something that exists */
271 if (!(t->ipos = h->next))
272 hnbucket(t);
273
274 /* recycle the deleted hitem node */
275 redel(t->space, h);
276
277#ifdef HSANITY
278 hsanity(t);
279#endif /* HSANITY */
280
281 return TRUE;
282}
283
284/**
285 * @brief Position the hash table on the first element.
286 *
287 * Sets the current position to the first item in the hash table.
288 *
289 * @param t Pointer to the hash table.
290 * @return TRUE if the first element exists, FALSE otherwise.
291 */
292word hfirst(t)
293 htab *t; /* the hash table */
294{
295 t->apos = t->mask;
296 (void)hnbucket(t);
297 return (t->ipos != (hitem *)0);
298}
299
300/* hnext() is a macro, see hashtab.h */
301
302/*
303 * hnbucket - Move position to the first item in the next bucket.
304 * Return TRUE if we did not wrap around to the beginning of the table
305 */
306word hnbucket(t)
307 htab *t;
308{
309 ub4 oldapos = t->apos;
310 ub4 end = (ub4)1 << (t->logsize);
311 ub4 i;
312
313 /* see if the element can be found without wrapping around */
314 for (i = oldapos + 1; i < end; ++i) {
315 if (t->table[i & t->mask]) {
316 t->apos = i;
317 t->ipos = t->table[i];
318 return TRUE;
319 }
320 }
321
322 /* must have to wrap around to find the last element */
323 for (i = 0; i <= oldapos; ++i) {
324 if (t->table[i]) {
325 t->apos = i;
326 t->ipos = t->table[i];
327 return FALSE;
328 }
329 }
330
331 return FALSE;
332}
333
334void hstat(t)
335 htab *t;
336{
337 ub4 i, j;
338 double total = 0.0;
339 hitem *h;
340 hitem *walk, *walk2, *stat = (hitem *)0;
341
342 /* in stat, keyl will store length of list, hval the number of buckets */
343 for (i = 0; i <= t->mask; ++i) {
344 for (h = t->table[i], j = 0; h; ++j, h = h->next)
345 ;
346 for (walk = stat; walk && (walk->keyl != j); walk = walk->next)
347 ;
348 if (walk) {
349 ++(walk->hval);
350 } else {
351 walk = (hitem *)renew(t->space);
352 walk->keyl = j;
353 walk->hval = 1;
354 if (!stat || stat->keyl > j) {
355 walk->next = stat;
356 stat = walk;
357 } else {
358 for (walk2 = stat; walk2->next && (walk2->next->keyl < j); walk2 = walk2->next)
359 ;
360 walk->next = walk2->next;
361 walk2->next = walk;
362 }
363 }
364 }
365
366 /* figure out average list length for existing elements */
367 for (walk = stat; walk; walk = walk->next) {
368 total += (double)walk->hval * (double)walk->keyl * (double)walk->keyl;
369 }
370 if (t->count)
371 total /= (double)t->count;
372 else
373 total = (double)0;
374
375 /* print statistics */
376 printf("\n");
377 for (walk = stat; walk; walk = walk->next) {
378 printf("items %ld: %ld buckets\n", walk->keyl, walk->hval);
379 }
380 printf("\nbuckets: %ld items: %ld existing: %g\n\n", ((ub4)1 << t->logsize), t->count, total);
381
382 /* clean up */
383 while (stat) {
384 walk = stat->next;
385 redel(t->space, stat);
386 stat = walk;
387 }
388}
word hfirst(htab *t)
Position the hash table on the first element.
Definition hashtab.c:292
htab * hcreate(word logsize)
Create a hash table.
Definition hashtab.c:124
word hadd(htab *t, ub1 *key, ub4 keyl, void *stuff)
Add an item to the hash table.
Definition hashtab.c:207
void hdestroy(htab *t)
Destroy the hash table and free all its memory.
Definition hashtab.c:149
word hfind(htab *t, ub1 *key, ub4 keyl)
Find an item with a given key in the hash table.
Definition hashtab.c:178