Annotation of embedaddon/trafshow/hashtab.c, revision 1.1
1.1 ! misho 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 <stdlib.h>
! 32: #include <string.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: #ifdef DEBUG
! 48: /* sanity check -- make sure ipos, apos, and count make sense */
! 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: fprintf(stderr, "hsanity: 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: fprintf(stderr, "hsanity: 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: fprintf(stderr, "hsanity: counter %ld, t->count %ld\n", counter, t->count);
! 76: }
! 77: #endif /* DEBUG */
! 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: if (!newtab) return;
! 95:
! 96: /* make sure newtab is cleared */
! 97: for (i=0; i<newsize; ++i) newtab[i] = (hitem *)0;
! 98: t->table = newtab;
! 99: t->mask = newmask;
! 100:
! 101: /* Walk through old table putting entries in new table */
! 102: for (i=newsize>>1; i--;)
! 103: {
! 104: register hitem *this, *that, **newplace;
! 105: for (this = oldtab[i]; this;)
! 106: {
! 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) */
! 124: htab *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: if (!t) return 0;
! 130:
! 131: len = ((ub4)1<<logsize);
! 132: t->table = (hitem **)malloc(sizeof(hitem *)*(ub4)len);
! 133: if (!t->table) return 0;
! 134: for (i=0; i<len; ++i) t->table[i] = (hitem *)0;
! 135: t->logsize = logsize;
! 136: t->mask = len-1;
! 137: t->count = 0;
! 138: t->apos = (ub4)0;
! 139: t->ipos = (hitem *)0;
! 140: t->space = remkroot(sizeof(hitem));
! 141: if (!t->space) return 0;
! 142: t->bcount = 0;
! 143: return t;
! 144: }
! 145:
! 146: /* hdestroy - destroy the hash table and free all its memory */
! 147: void hdestroy( t)
! 148: htab *t; /* the table */
! 149: {
! 150: refree(t->space);
! 151: free((char *)t->table);
! 152: free((char *)t);
! 153: }
! 154:
! 155: /* hcount() is a macro, see hashtab.h */
! 156: /* hkey() is a macro, see hashtab.h */
! 157: /* hkeyl() is a macro, see hashtab.h */
! 158: /* hstuff() is a macro, see hashtab.h */
! 159:
! 160: /* hfind - find an item with a given key in a hash table */
! 161: word hfind( t, key, keyl )
! 162: htab *t; /* table */
! 163: ub1 *key; /* key to find */
! 164: ub4 keyl; /* key length */
! 165: {
! 166: hitem *h;
! 167: ub4 x = lookup(key,keyl,0);
! 168: ub4 y;
! 169: for (h = t->table[y=(x&t->mask)]; h; h = h->next)
! 170: {
! 171: if ((x == h->hval) &&
! 172: (keyl == h->keyl) &&
! 173: !memcmp(key, h->key, keyl))
! 174: {
! 175: t->apos = y;
! 176: t->ipos = h;
! 177: return TRUE;
! 178: }
! 179: }
! 180: return FALSE;
! 181: }
! 182:
! 183: /*
! 184: * hadd - add an item to a hash table.
! 185: * return FALSE if the key is already there, otherwise TRUE.
! 186: */
! 187: word hadd( t, key, keyl, stuff)
! 188: htab *t; /* table */
! 189: ub1 *key; /* key to add to hash table */
! 190: ub4 keyl; /* key length */
! 191: void *stuff; /* stuff to associate with this key */
! 192: {
! 193: register hitem *h,**hp;
! 194: register ub4 y, x = lookup(key,keyl,0);
! 195:
! 196: /* make sure the key is not already there */
! 197: for (h = t->table[(y=(x&t->mask))]; h; h = h->next)
! 198: {
! 199: if ((x == h->hval) &&
! 200: (keyl == h->keyl) &&
! 201: !memcmp(key, h->key, keyl))
! 202: {
! 203: t->apos = y;
! 204: t->ipos = h;
! 205: return FALSE;
! 206: }
! 207: }
! 208:
! 209: /* find space for a new item */
! 210: h = (hitem *)renew(t->space);
! 211: if (!h) return -1;
! 212:
! 213: /* make the hash table bigger if it is getting full */
! 214: if (++t->count > (ub4)1<<(t->logsize))
! 215: {
! 216: hgrow(t);
! 217: y = (x&t->mask);
! 218: }
! 219:
! 220: /* add the new key to the table */
! 221: h->key = key;
! 222: h->keyl = keyl;
! 223: h->stuff = stuff;
! 224: h->hval = x;
! 225: hp = &t->table[y];
! 226: h->next = *hp;
! 227: *hp = h;
! 228: t->ipos = h;
! 229: t->apos = y;
! 230:
! 231: #ifdef DEBUG
! 232: hsanity(t);
! 233: #endif /* DEBUG */
! 234:
! 235: return TRUE;
! 236: }
! 237:
! 238: /* hdel - delete the item at the current position */
! 239: word hdel(t)
! 240: htab *t; /* the hash table */
! 241: {
! 242: hitem *h; /* item being deleted */
! 243: hitem **ip; /* a counter */
! 244:
! 245: /* check for item not existing */
! 246: if (!(h = t->ipos)) return FALSE;
! 247:
! 248: /* remove item from its list */
! 249: for (ip = &t->table[t->apos]; *ip != h; ip = &(*ip)->next)
! 250: ;
! 251: *ip = (*ip)->next;
! 252: --(t->count);
! 253:
! 254: /* adjust position to something that exists */
! 255: if (!(t->ipos = h->next)) hnbucket(t);
! 256:
! 257: /* recycle the deleted hitem node */
! 258: redel(t->space, h);
! 259:
! 260: #ifdef DEBUG
! 261: hsanity(t);
! 262: #endif /* DEBUG */
! 263:
! 264: return TRUE;
! 265: }
! 266:
! 267: /* hfirst - position on the first element in the table */
! 268: word hfirst(t)
! 269: htab *t; /* the hash table */
! 270: {
! 271: t->apos = t->mask;
! 272: (void)hnbucket(t);
! 273: return (t->ipos != (hitem *)0);
! 274: }
! 275:
! 276: /* hnext() is a macro, see hashtab.h */
! 277:
! 278: /*
! 279: * hnbucket - Move position to the first item in the next bucket.
! 280: * Return TRUE if we did not wrap around to the beginning of the table
! 281: */
! 282: word hnbucket(t)
! 283: htab *t;
! 284: {
! 285: ub4 oldapos = t->apos;
! 286: ub4 end = (ub4)1<<(t->logsize);
! 287: ub4 i;
! 288:
! 289: /* see if the element can be found without wrapping around */
! 290: for (i=oldapos+1; i<end; ++i)
! 291: {
! 292: if (t->table[i&t->mask])
! 293: {
! 294: t->apos = i;
! 295: t->ipos = t->table[i];
! 296: return TRUE;
! 297: }
! 298: }
! 299:
! 300: /* must have to wrap around to find the last element */
! 301: for (i=0; i<=oldapos; ++i)
! 302: {
! 303: if (t->table[i])
! 304: {
! 305: t->apos = i;
! 306: t->ipos = t->table[i];
! 307: return FALSE;
! 308: }
! 309: }
! 310:
! 311: return FALSE;
! 312: }
! 313:
! 314: void hstat(fp, t)
! 315: FILE *fp;
! 316: htab *t;
! 317: {
! 318: ub4 i,j;
! 319: double total = 0.0;
! 320: hitem *h;
! 321: hitem *walk, *walk2, *stat = (hitem *)0;
! 322:
! 323: /* in stat, keyl will store length of list, hval the number of buckets */
! 324: for (i=0; i<=t->mask; ++i)
! 325: {
! 326: for (h=t->table[i], j=0; h; ++j, h=h->next)
! 327: ;
! 328: for (walk=stat; walk && (walk->keyl != j); walk=walk->next)
! 329: ;
! 330: if (walk)
! 331: {
! 332: ++(walk->hval);
! 333: }
! 334: else
! 335: {
! 336: walk = (hitem *)renew(t->space);
! 337: if (!walk) {
! 338: fprintf(fp, "renew: Can't allocate memory?\n");
! 339: return;
! 340: }
! 341: walk->keyl = j;
! 342: walk->hval = 1;
! 343: if (!stat || stat->keyl > j) {walk->next=stat; stat=walk;}
! 344: else
! 345: {
! 346: for (walk2=stat;
! 347: walk2->next && (walk2->next->keyl<j);
! 348: walk2=walk2->next)
! 349: ;
! 350: walk->next = walk2->next;
! 351: walk2->next = walk;
! 352: }
! 353: }
! 354: }
! 355:
! 356: /* figure out average list length for existing elements */
! 357: for (walk=stat; walk; walk=walk->next)
! 358: {
! 359: total+=(double)walk->hval*(double)walk->keyl*(double)walk->keyl;
! 360: }
! 361: if (t->count) total /= (double)t->count;
! 362: else total = (double)0;
! 363:
! 364: /* print statistics */
! 365: fprintf(fp, "\n");
! 366: for (walk=stat; walk; walk=walk->next)
! 367: {
! 368: fprintf(fp, "items %ld: %ld buckets\n", walk->keyl, walk->hval);
! 369: }
! 370: fprintf(fp, "\nbuckets: %ld items: %ld existing: %g\n\n",
! 371: ((ub4)1<<t->logsize), t->count, total);
! 372:
! 373: /* clean up */
! 374: while (stat)
! 375: {
! 376: walk = stat->next;
! 377: redel(t->space, stat);
! 378: stat = walk;
! 379: }
! 380: }
! 381:
! 382:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>