ps2sdk  1.1
A collection of Open Source libraries used for developing applications on Sony's PlayStation 2® (PS2).
hashtab.c
Go to the documentation of this file.
1 /*
2 --------------------------------------------------------------------
3 By Bob Jenkins, 1996. hashtab.c. Public Domain.
4 
5 This 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 <malloc.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
49 static void hsanity(t)
50 htab *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 %ld apos %ld\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 %ld\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 %ld t->count %ld\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  */
86 static void hgrow( t)
87 htab *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, **newplace;
104  for (this = oldtab[i]; this;)
105  {
106  that = this;
107  this = this->next;
108  newplace = &newtab[(that->hval & newmask)];
109  that->next = *newplace;
110  *newplace = that;
111  }
112  }
113 
114  /* position the hash table on some existing item */
115  hfirst(t);
116 
117  /* free the old array */
118  free((char *)oldtab);
119 
120 }
121 
122 /* hcreate - create a hash table initially of size power(2,logsize) */
123 htab *hcreate(logsize)
124 word logsize; /* log base 2 of the size of the hash table */
125 {
126  ub4 i,len;
127  htab *t = (htab *)malloc(sizeof(htab));
128 
129  len = ((ub4)1<<logsize);
130  t->table = (hitem **)malloc(sizeof(hitem *)*(ub4)len);
131  for (i=0; i<len; ++i) t->table[i] = (hitem *)0;
132  t->logsize = logsize;
133  t->mask = len-1;
134  t->count = 0;
135  t->apos = (ub4)0;
136  t->ipos = (hitem *)0;
137  t->space = remkroot(sizeof(hitem));
138  t->bcount = 0;
139  return t;
140 }
141 
142 /* hdestroy - destroy the hash table and free all its memory */
143 void hdestroy( t)
144 htab *t; /* the table */
145 {
146  refree(t->space);
147  free((char *)t->table);
148  free((char *)t);
149 }
150 
151 /* hcount() is a macro, see hashtab.h */
152 /* hkey() is a macro, see hashtab.h */
153 /* hkeyl() is a macro, see hashtab.h */
154 /* hstuff() is a macro, see hashtab.h */
155 
156 /* hfind - find an item with a given key in a hash table */
157 word hfind( t, key, keyl )
158 htab *t; /* table */
159 ub1 *key; /* key to find */
160 ub4 keyl; /* key length */
161 {
162  hitem *h;
163  ub4 x = lookup(key,keyl,0);
164  ub4 y;
165  for (h = t->table[y=(x&t->mask)]; h; h = h->next)
166  {
167  if ((x == h->hval) &&
168  (keyl == h->keyl) &&
169  !memcmp(key, h->key, keyl))
170  {
171  t->apos = y;
172  t->ipos = h;
173  return TRUE;
174  }
175  }
176  return FALSE;
177 }
178 
179 /*
180  * hadd - add an item to a hash table.
181  * return FALSE if the key is already there, otherwise TRUE.
182  */
183 word hadd( t, key, keyl, stuff)
184 htab *t; /* table */
185 ub1 *key; /* key to add to hash table */
186 ub4 keyl; /* key length */
187 void *stuff; /* stuff to associate with this key */
188 {
189  register hitem *h,**hp;
190  register ub4 y, x = lookup(key,keyl,0);
191 
192  /* make sure the key is not already there */
193  for (h = t->table[(y=(x&t->mask))]; h; h = h->next)
194  {
195  if ((x == h->hval) &&
196  (keyl == h->keyl) &&
197  !memcmp(key, h->key, keyl))
198  {
199  t->apos = y;
200  t->ipos = h;
201  return FALSE;
202  }
203  }
204 
205  /* find space for a new item */
206  h = (hitem *)renew(t->space);
207 
208  /* make the hash table bigger if it is getting full */
209  if (++t->count > (ub4)1<<(t->logsize))
210  {
211  hgrow(t);
212  y = (x&t->mask);
213  }
214 
215  /* add the new key to the table */
216  h->key = key;
217  h->keyl = keyl;
218  h->stuff = stuff;
219  h->hval = x;
220  hp = &t->table[y];
221  h->next = *hp;
222  *hp = h;
223  t->ipos = h;
224  t->apos = y;
225 
226 #ifdef HSANITY
227  hsanity(t);
228 #endif /* HSANITY */
229 
230  return TRUE;
231 }
232 
233 /* hdel - delete the item at the current position */
235 htab *t; /* the hash table */
236 {
237  hitem *h; /* item being deleted */
238  hitem **ip; /* a counter */
239 
240  /* check for item not existing */
241  if (!(h = t->ipos)) return FALSE;
242 
243  /* remove item from its list */
244  for (ip = &t->table[t->apos]; *ip != h; ip = &(*ip)->next)
245  ;
246  *ip = (*ip)->next;
247  --(t->count);
248 
249  /* adjust position to something that exists */
250  if (!(t->ipos = h->next)) hnbucket(t);
251 
252  /* recycle the deleted hitem node */
253  redel(t->space, h);
254 
255 #ifdef HSANITY
256  hsanity(t);
257 #endif /* HSANITY */
258 
259  return TRUE;
260 }
261 
262 /* hfirst - position on the first element in the table */
264 htab *t; /* the hash table */
265 {
266  t->apos = t->mask;
267  (void)hnbucket(t);
268  return (t->ipos != (hitem *)0);
269 }
270 
271 /* hnext() is a macro, see hashtab.h */
272 
273 /*
274  * hnbucket - Move position to the first item in the next bucket.
275  * Return TRUE if we did not wrap around to the beginning of the table
276  */
278 htab *t;
279 {
280  ub4 oldapos = t->apos;
281  ub4 end = (ub4)1<<(t->logsize);
282  ub4 i;
283 
284  /* see if the element can be found without wrapping around */
285  for (i=oldapos+1; i<end; ++i)
286  {
287  if (t->table[i&t->mask])
288  {
289  t->apos = i;
290  t->ipos = t->table[i];
291  return TRUE;
292  }
293  }
294 
295  /* must have to wrap around to find the last element */
296  for (i=0; i<=oldapos; ++i)
297  {
298  if (t->table[i])
299  {
300  t->apos = i;
301  t->ipos = t->table[i];
302  return FALSE;
303  }
304  }
305 
306  return FALSE;
307 }
308 
309 #ifdef HSTAT
310 void hstat(t)
311 htab *t;
312 {
313  ub4 i,j;
314  double total = 0.0;
315  hitem *h;
316  hitem *walk, *walk2, *stat = (hitem *)0;
317 
318  /* in stat, keyl will store length of list, hval the number of buckets */
319  for (i=0; i<=t->mask; ++i)
320  {
321  for (h=t->table[i], j=0; h; ++j, h=h->next)
322  ;
323  for (walk=stat; walk && (walk->keyl != j); walk=walk->next)
324  ;
325  if (walk)
326  {
327  ++(walk->hval);
328  }
329  else
330  {
331  walk = (hitem *)renew(t->space);
332  walk->keyl = j;
333  walk->hval = 1;
334  if (!stat || stat->keyl > j) {walk->next=stat; stat=walk;}
335  else
336  {
337  for (walk2=stat;
338  walk2->next && (walk2->next->keyl<j);
339  walk2=walk2->next)
340  ;
341  walk->next = walk2->next;
342  walk2->next = walk;
343  }
344  }
345  }
346 
347  /* figure out average list length for existing elements */
348  for (walk=stat; walk; walk=walk->next)
349  {
350  total+=(double)walk->hval*(double)walk->keyl*(double)walk->keyl;
351  }
352  if (t->count) total /= (double)t->count;
353  else total = (double)0;
354 
355  /* print statistics */
356  printf("\n");
357  for (walk=stat; walk; walk=walk->next)
358  {
359  printf("items %ld: %ld buckets\n", walk->keyl, walk->hval);
360  }
361  printf("\nbuckets: %ld items: %ld existing: %g\n\n",
362  ((ub4)1<<t->logsize), t->count, total);
363 
364  /* clean up */
365  while (stat)
366  {
367  walk = stat->next;
368  redel(t->space, stat);
369  stat = walk;
370  }
371 }
372 #endif
#define TRUE
Definition: callstack.c:109
#define FALSE
Definition: callstack.c:112
word hdel(htab *t)
Definition: hashtab.c:234
word hfirst(htab *t)
Definition: hashtab.c:263
word hnbucket(htab *t)
Definition: hashtab.c:277
static void hgrow(htab *t)
Definition: hashtab.c:86
word hadd(htab *t, ub1 *key, ub4 keyl, void *stuff)
Definition: hashtab.c:183
htab * hcreate(word logsize)
Definition: hashtab.c:123
void hdestroy(htab *t)
Definition: hashtab.c:143
word hfind(htab *t, ub1 *key, ub4 keyl)
Definition: hashtab.c:157
void hstat()
s32 x
Definition: libmouse.c:34
s32 y
Definition: libmouse.c:34
ub4 lookup()
#define redel(root, item)
Definition: recycle.h:60
#define renew(r)
Definition: recycle.h:53
reroot * remkroot()
void refree()
u8 ub1
Definition: standard.h:36
u32 ub4
Definition: standard.h:32
int word
Definition: standard.h:48
Definition: hashtab.h:45
struct hitem * next
Definition: hashtab.h:50
void * stuff
Definition: hashtab.h:48
ub4 keyl
Definition: hashtab.h:47
ub4 hval
Definition: hashtab.h:49
ub1 * key
Definition: hashtab.h:46
Definition: hashtab.h:56
struct hitem * ipos
Definition: hashtab.h:62
struct reroot * space
Definition: hashtab.h:63
ub4 bcount
Definition: hashtab.h:64
size_t mask
Definition: hashtab.h:59
word logsize
Definition: hashtab.h:58
ub4 count
Definition: hashtab.h:60
ub4 apos
Definition: hashtab.h:61
struct hitem ** table
Definition: hashtab.h:57