home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / ns2tab / part02 / hash.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-04  |  11.3 KB  |  483 lines

  1. #include <stdio.h>
  2.  
  3. /*
  4. **
  5. **    Copyright (c) 1987, Robert L. McQueer
  6. **        All Rights Reserved
  7. **
  8. ** Permission granted for use, modification and redistribution of this
  9. ** software provided that no use is made for commercial gain without the
  10. ** written consent of the author, that all copyright notices remain intact,
  11. ** and that all changes are clearly documented.  No warranty of any kind
  12. ** concerning any use which may be made of this software is offered or implied.
  13. **
  14. */
  15.  
  16. /*
  17. ** generic hash table routines.
  18. **
  19. **    htab_init - initialize a new hash table
  20. **    htab_free - destroy a hash table, freeing associated memory
  21. **    htab_clear - remove all hash table entries
  22. **    htab_find - find entry
  23. **    htab_del - delete entry
  24. **    htab_enter - enter item into table
  25. **    htab_list - scan hash table entries.
  26. **
  27. ** Multiple hash tables may be used.  Caller may provide key comparison
  28. ** and hash routines, or use defaults.
  29. */
  30.  
  31. /*
  32. ** HASHMAGIC define may be used to compile a version of these routines
  33. ** which will catch bad context blocks passed in by client routines
  34. ** through a magic number check.  If defined, HASHMAGIC should be the
  35. ** actual number to use for a magic number.  Configurable so that you
  36. ** may avoid the overhead of checking it all the time in these routines
  37. ** which may have high entry rates.
  38. */
  39.  
  40. /*
  41. ** allocation: nodes are allocated starting with a block of ALLOCINIT,
  42. ** doubling the size for the next allocation as long as the size is strictly
  43. ** less than ALLOCLIMIT.  If you make ALLOCLIMIT a value encountered by
  44. ** successive doubling of ALLOCINIT, that will be the final size, otherwise the
  45. ** next doubling larger.
  46. **
  47. ** The idea of this stunt is that we don't know whether the caller is going
  48. ** to make a lot of entries, or just a few.  So we start out allocating
  49. ** just a few nodes at a crack, and as the caller makes more and more
  50. ** entries, allocate bigger bunches.  For memory-restrictive environments
  51. ** like MS-DOS, one could set ALLOCLIMIT low & simply pay the penalty for
  52. ** lots of malloc calls.
  53. */
  54. #define ALLOCINIT 25
  55. #define ALLOCLIMIT 800
  56.  
  57. #define MAGCHECK(T,N) if (T->magic != HASHMAGIC) fatal(Merr,N)
  58.  
  59. typedef struct _htab
  60. {
  61.     struct _htab *next;
  62.     char *key;
  63.     char *data;
  64. } HASHTAB;
  65.  
  66. typedef struct _faddr
  67. {
  68.     struct _faddr *next;
  69. } FADDR;
  70.  
  71. /*
  72. ** fpool, tpool - tpool is the pool of available nodes.  Every time
  73. ** a new block is allocated, one FADDR is allocated along with it.
  74. ** The address allocated is coerced into the FADDR and placed on fpool
  75. ** to facilitate freeing.
  76. */
  77.  
  78. typedef struct
  79. {
  80. #ifdef HASHMAGIC
  81.     int magic;
  82. #endif
  83.     HASHTAB **tab;        /* hash table */
  84.     HASHTAB *tpool;        /* available nodes */
  85.     HASHTAB *srch;        /* current search pointer for htab_list */
  86.     FADDR *fpool;        /* alloc pointers for htab_free */
  87.     int (*afail)();        /* allocation error handler */
  88.     int (*compare)();    /* comparison routine */
  89.     int (*hashfunc)();    /* hash function */
  90.     int size;        /* size of table (length of tab item) */
  91.     int ablock;        /* current allocation block size */
  92.     int srchidx;        /* current table index for htab_list */
  93. } CONTEXT;
  94. #ifdef __STDC__
  95. #include <stdlib.h>
  96. #else
  97. extern char *malloc();
  98. #endif
  99. static HASHTAB * get_node(CONTEXT *);
  100. static int hash(register char *,int);
  101. static int def_afail();
  102.  
  103. #ifdef HASHMAGIC
  104. static char *Merr = "Bad magic number in hash table context block, %s()";
  105. #endif
  106.  
  107. /*
  108. ** free hash table.  tab is pointer returned by htab_init.
  109. */
  110. htab_free(void *tab)
  111. {
  112.     register FADDR *ptr, *next;
  113.     int i;
  114.     register CONTEXT *cb;
  115.  
  116.     cb = (CONTEXT *) tab;
  117.  
  118. #ifdef HASHMAGIC
  119.     MAGCHECK(cb,"htab_free");
  120.     ++(cb->magic);
  121. #endif
  122.  
  123.     for (ptr = cb->fpool; ptr != NULL; ptr = next)
  124.     {
  125.         next = ptr->next;
  126.         free ((char *) ptr);
  127.     }
  128.  
  129.     free (tab);
  130. }
  131.  
  132. /*
  133. ** remove all hash table entries.  Does not free memory, simply restores
  134. ** empty table, as if one had called htab_delete on all the keys.  tab is
  135. ** pointer returned by htab_init.
  136. */
  137. htab_clear(void *tab)
  138. {
  139.     register CONTEXT *cb;
  140.     register HASHTAB **tptr;
  141.     register HASHTAB *nptr;
  142.     int i;
  143.  
  144.     cb = (CONTEXT *) tab;
  145.  
  146. #ifdef HASHMAGIC
  147.     MAGCHECK(cb,"htab_clear");
  148. #endif
  149.  
  150.     tptr = cb->tab;
  151.  
  152.     for (i=0; i < cb->size; ++i,++tptr)
  153.     {
  154.         nptr = *tptr;
  155.         if (nptr == NULL)
  156.             continue;
  157.         while (nptr->next != NULL)
  158.             nptr = nptr->next;
  159.         nptr->next = cb->tpool;
  160.         cb->tpool = *tptr;
  161.         *tptr = NULL;
  162.     }
  163.  
  164.     /* force any open htab_list's to return empty */
  165.     cb->srch = NULL;
  166.     cb->srchidx = cb->size;
  167. }
  168.  
  169. /*
  170. ** initialize a hash table.  Returns a pointer to be used with other
  171. ** calls, NULL for failure.
  172. **
  173. ** The hash table will be maintained as a linked list for each node,
  174. ** so any number of entries can be made to it, whatever the value for
  175. ** size (>100% density is perfectly OK).
  176. **
  177. **    size - size of table.  If hfunc is NULL, will be incremented
  178. **        up to a prime size to suit the type of hash function
  179. **        used by default.  Caller may find out the actual size
  180. **        by calling next_prime().
  181. **
  182. **    allocfail - routine to call in case of memory allocation failure.
  183. **        If NULL, allocation failure will make a call to fatal().
  184. **
  185. **    comp - routine used to compare keys.  returns 0 if equal, non-zero
  186. **        otherwise.  If NULL, strcmp() will be used.  Your own will
  187. **        have to be provided if your keys are something other than
  188. **        strings.  These routines will always call this with the
  189. **        comparison key as the first argument, and the one in the
  190. **        table already as a second argument.  This fact is most
  191. **        useful for making comparisons up to the length of the entered
  192. **        key, for instance.
  193. **
  194. **    hfunc - hash function.  called as (*hfunc)(key, size).  size argument
  195. **        may be ignored if function was written for a specific size.
  196. **        Must return an integer between 0 and size-1.  If NULL, the
  197. **        default hash function is the typical "string-divided-modulo
  198. **        -table-size" algorithm.
  199. */
  200. void *
  201. htab_init(size,allocfail,comp,hfunc)
  202. int size;
  203. int (*allocfail)();
  204. int (*comp)();
  205. int (*hfunc)();
  206. {
  207.     int def_afail();
  208.     int strcmp();
  209.     int i;
  210.     CONTEXT *cb;
  211.  
  212.     if (allocfail == NULL)
  213.         allocfail = def_afail;
  214.  
  215.     if (comp == NULL)
  216.         comp = strcmp;
  217.  
  218.     if (hfunc == NULL)
  219.     {
  220.         size = next_prime(size);
  221.         hfunc = hash;
  222.     }
  223.  
  224.     i = sizeof(CONTEXT) + size * sizeof(HASHTAB *);
  225.  
  226.     if ((cb = (CONTEXT *) malloc(i)) == NULL)
  227.     {
  228.         (*allocfail)();
  229.         return (NULL);
  230.     }
  231.  
  232. #ifdef HASHMAGIC
  233.     cb->magic = HASHMAGIC;
  234. #endif
  235.  
  236.     cb->afail = allocfail;
  237.     cb->compare = comp;
  238.     cb->hashfunc = hfunc;
  239.     cb->size = size;
  240.     cb->tab = (HASHTAB **)(cb+1);
  241.  
  242.     for (i=0; i < cb->size; ++i)
  243.         (cb->tab)[i] = NULL;
  244.     cb->tpool = NULL;
  245.     cb->fpool = NULL;
  246.     cb->ablock = ALLOCINIT;
  247.  
  248.     /* safety, in case somebody calls htab_list wrong */
  249.     cb->srch = NULL;
  250.     cb->srchidx = size;
  251.  
  252.     return ((char *) cb);
  253. }
  254.  
  255.  
  256. /*
  257. ** find an entry in hash table.  The pointer returned is NULL for
  258. ** failure, or the data pointer associated with the key in htab_enter.
  259. **
  260. **    tab - table pointer returned by htab_init.
  261. **    s - search key.
  262. */
  263. char *
  264. htab_find(void *tab,void *s)
  265. {
  266.     register HASHTAB *ptr;
  267.     register CONTEXT *cb;
  268.  
  269.     cb = (CONTEXT *) tab;
  270.  
  271. #ifdef HASHMAGIC
  272.     MAGCHECK(cb,"htab_find");
  273. #endif
  274.  
  275.     for (ptr = (cb->tab)[(*(cb->hashfunc))(s,cb->size)];
  276.                     ptr != NULL; ptr = ptr->next)
  277.     {
  278.         if ((*(cb->compare))(s,ptr->key) == 0)
  279.             return (ptr->data);
  280.     }
  281.  
  282.     return (NULL);
  283. }
  284.  
  285. /*
  286. ** delete a hash table entry.  Returns 0 for success, <0 for no entry.
  287. **
  288. **    tab - table pointer returned by htab_init.
  289. **    s - search key.
  290. */
  291. htab_del(void *tab,void *s)
  292. {
  293.     register HASHTAB *ptr;
  294.     register CONTEXT *cb;
  295.     register HASHTAB *pred;
  296.     int idx;
  297.  
  298.     cb = (CONTEXT *) tab;
  299.  
  300. #ifdef HASHMAGIC
  301.     MAGCHECK(cb,"htab_del");
  302. #endif
  303.  
  304.     pred = NULL;
  305.     for (ptr = (cb->tab)[idx = (*(cb->hashfunc))(s,cb->size)];
  306.                         ptr != NULL; ptr = ptr->next)
  307.     {
  308.         if ((*(cb->compare))(s,ptr->key) == 0)
  309.             break;
  310.         pred = ptr;
  311.     }
  312.  
  313.     if (ptr == NULL)
  314.         return (-1);
  315.  
  316.     /*
  317.     ** if we're deleting the current search index in the middle
  318.     ** of an htab_list, go to next item.
  319.     */
  320.     if (ptr == cb->srch)
  321.         cb->srch = ptr->next;
  322.  
  323.     if (pred == NULL)
  324.         (cb->tab)[idx] = ptr->next;
  325.     else
  326.         pred->next = ptr->next;
  327.     ptr->next = cb->tpool;
  328.     cb->tpool = ptr;
  329.  
  330.     return (0);
  331. }
  332.  
  333. /*
  334. ** enter new item into hash table:
  335. **
  336. **    tab - table pointer from htab_init.
  337. **    s - key.
  338. **    data - data to associate with key.  In most cases, will probably
  339. **        actually be a pointer to some sort of structure known
  340. **        to the caller.
  341. **
  342. **    both s and data should point to storage valid for the entire life of
  343. **    the table.  htab_enter can not allocate copies of either of these
  344. **    things since it does not know their structure (if you provided 
  345. **    comparison & hash routines, the key may not actually be a string).
  346. **    htab_enter WILL allocate actual table nodes.  Returns 0 for success,
  347. **    -1 for failure.  Failure return is possible only if allocation
  348. **    failure occurs, and was not set up as fatal in htab_init().
  349. */
  350. htab_enter(void *tab,void *s,void *data)
  351. {
  352.     register HASHTAB *ptr;
  353.     register CONTEXT *cb;
  354.     int i;
  355.  
  356.     cb = (CONTEXT *) tab;
  357.  
  358. #ifdef HASHMAGIC
  359.     MAGCHECK(cb,"htab_enter");
  360. #endif
  361.  
  362.     if ((ptr = get_node(cb)) == NULL)
  363.         return (-1);
  364.  
  365.     ptr->next = (cb->tab)[i = (*(cb->hashfunc))(s,cb->size)];
  366.     (cb->tab)[i] = ptr;
  367.     ptr->key = s;
  368.     ptr->data = data;
  369.     return (0);
  370. }
  371.  
  372. /*
  373. ** Routine to scan all hash table entries through successive calls.
  374. ** Returns 1 if an entry found, 0 for no more entries.  Will not
  375. ** be returned in any particular order comprehensible to the
  376. ** calling program (hash table order).
  377. **
  378. **    tab - table pointer from htab_init
  379. **    first - 1 to start scan, 0 on succesive calls.
  380. **    data, key - returned data and key.
  381. **
  382. ** Precautions have been taken to allow interleave of this routine with
  383. ** htab_del and htab_clear, but the only interleave that truly makes
  384. ** sense is to selectively htab_del() entries on some basis as they
  385. ** come back from htab_list().  htab_enter()'s in mid list scan may be
  386. ** done, but they may or may not show up on following calls, dependent
  387. ** on where they were entered in relation to the current list pointer.
  388. **
  389. ** This routine sets a global variable on all successful calls:
  390. **
  391. **    int Htab_Index; hash table index entry was found at.
  392. **
  393. ** By examining this while scanning the list of entries, a caller may
  394. ** obtain statistics on table distribution.  The value will increase
  395. ** monotonically as the search proceeds, skipping across indices
  396. ** with no entries.
  397. */
  398.  
  399. int Htab_Index;
  400.  
  401. htab_list(void *tab,int first,void **data,void **key)
  402. {
  403.     register CONTEXT *cb;
  404.  
  405.     cb = (CONTEXT *) tab;
  406.  
  407. #ifdef HASHMAGIC
  408.     MAGCHECK(cb,"htab_list");
  409. #endif
  410.  
  411.     if (first)
  412.     {
  413.         cb->srch = NULL;
  414.         cb->srchidx = -1;
  415.     }
  416.  
  417.     while (cb->srch == NULL)
  418.     {
  419.         ++(cb->srchidx);
  420.         if (cb->srchidx >= cb->size)
  421.             return (0);
  422.         cb->srch = (cb->tab)[cb->srchidx];
  423.     }
  424.  
  425.     Htab_Index = cb->srchidx;
  426.  
  427.     *data = (cb->srch)->data;
  428.     *key = (cb->srch)->key;
  429.  
  430.     cb->srch = (cb->srch)->next;
  431.     return(1);
  432. }
  433.  
  434. static HASHTAB *
  435. get_node(CONTEXT *cb)
  436. {
  437.     char *addr;
  438.     HASHTAB *ptr;
  439.     int i;
  440.  
  441.     if (cb->tpool == NULL)
  442.     {
  443.         addr = malloc((cb->ablock)*sizeof(HASHTAB)+sizeof(FADDR));
  444.         if (addr == NULL)
  445.         {
  446.             (*(cb->afail))();
  447.             return (NULL);
  448.         }
  449.  
  450.         ((FADDR *) addr)->next = cb->fpool;
  451.         cb->fpool = (FADDR *) addr;
  452.         addr += sizeof(FADDR);
  453.         cb->tpool = (HASHTAB *) addr;
  454.         for (i=1; i < cb->ablock; ++i)
  455.             (cb->tpool)[i-1].next = cb->tpool + i;
  456.         (cb->tpool)[i-1].next = NULL;
  457.  
  458.         if (cb->ablock < ALLOCLIMIT)
  459.             cb->ablock *= 2;
  460.     }
  461.  
  462.     ptr = cb->tpool;
  463.     cb->tpool = (cb->tpool)->next;
  464.     return (ptr);
  465. }
  466.  
  467. static int
  468. hash(register char *s,int size)
  469. {
  470.     register long rem;
  471.  
  472.     for (rem = 0; *s != '\0'; ++s)
  473.         rem = (rem * 128 + *s) % size;
  474.     return(rem);
  475. }
  476.  
  477. static int
  478. def_afail()
  479. {
  480.     fprintf(stderr,"Memory allocation failure in hash table routines\n");
  481.     exit(1);
  482. }
  483.