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