home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume14 / bsd-dyna-link / assoc.c < prev    next >
C/C++ Source or Header  |  1988-05-08  |  17KB  |  744 lines

  1. #include <ctype.h>
  2. #include <stdio.h>
  3.  
  4.  
  5. #define PANIC_FILE stderr
  6.  
  7. #include "assoc.h"
  8.  
  9. int* H_getmem();
  10. int* assoc_lookup();
  11.  
  12.  
  13. #define LOWER(ch) (isupper(ch)? tolower(ch) : (ch))
  14.  
  15. /************************************************************************\
  16. **************************************************************************
  17. **                                    **
  18. **  This package uses hash tables to implement an associative memory,    **
  19. **  or "name table".  See also "assoc.h" and "assoc_internals.h".    **
  20. **                                    **
  21. **  The user may associate names, also called "index strings" with    **
  22. **  packets of memory, called "mem_cell"s, which come in fixed sizes.    **
  23. **  Then if you know the name, you can look up the mem_cell or vice    **
  24. **  versa.                                **
  25. **                                    **
  26. **  The collision recovery mechanism is a nifty little scheme           **
  27. **  of my own invention, which I call "linear-congruential rehash",    **
  28. **  which means "add one and multiply by three".            **
  29. **                                    **
  30. **  The orbit of the rehash function touches exactly half the entries    **
  31. **  in the table, and the table is never allowed to be over half full,  **
  32. **  so there will always be an empty slot for a new entry.        **
  33. **  Removing entries is a little bit tricky. Since the lookup mechanism **
  34. **  stops and reports failure when it comes to an empty slot in the     **
  35. **  rehash orbit for the value searched for, when an entry is removed,    **
  36. **  the values in the same orbit which are there as a result of a     **
  37. **  collision must be backed up to fill the evacuated slot.          **
  38. **  The entry number is also there for removing entries.  It provides a **
  39. **  reference back to the slot which points to the entry.        **
  40. **                                                                      **
  41. **  We assume that our machine uses two's complement arithmetic.    **
  42. **************************************************************************
  43. \************************************************************************/
  44.  
  45. /**************************************************************************
  46. **
  47. **  An entry looks like this:
  48. **
  49. **
  50. **  [pointers]      [pointees]
  51. **
  52. **          ------------------
  53. **  mem         -> |  entry number  |  index of entry into hash table.
  54. **          ------------------
  55. **  mem_cell ->    |             |
  56. **  (slot)    |         |
  57. **        | user's goodies |  of size table->value_size
  58. **            |         |
  59. **        ------------------
  60. **  string   -> |         |
  61. **        |  index string  |
  62. **        |         |
  63. **        |         |
  64. **        ------------------
  65. **
  66. **
  67. **  See string_from_cell(), cell_from_string(), mem_from_cell(), and
  68. **  cell_from_mem().. all macros.
  69. **
  70. \*************************************************************************/
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77.  
  78. /* N.B. The routine assoc() depends on the fact that if a table is less
  79. ** than half full, the rehash orbit will always find an empty slot.
  80. ** This rehash will hit exactly half the slots before it repeats, provided
  81. ** that the table length is a power of two.
  82. */
  83. #define REHASH(hash,table) (((hash+1)*3) & table -> mask)
  84.  
  85.  
  86.  
  87.  
  88. /**** factory-procedure, makes new tables. ****/
  89.  
  90. assoc_mem
  91. new_assoc_mem(value_size)
  92.   int value_size; /* storage size of values to be in assoc mem */
  93. {
  94.   register assoc_mem table;
  95.  
  96.   table = (assoc_mem) H_getmem(sizeof (struct assoc_mem_rec));
  97.  
  98.   table->value_size = value_size;
  99.   table->entries = 0;
  100.   table->size = INIT_TABLE_SIZE;
  101.   table->size_div_2 = table->size / 2;
  102.   table->mask = table->size -1;
  103.   table->array = (hash_table *)H_getmem(table->size * (sizeof (mem_cell)));
  104.  
  105.   return table;
  106. }
  107.  
  108.  
  109.  
  110. /* Associates a name with a new mem_cell, or return pointers to previously
  111. ** associated cell.  Initializes new cells to all zero, so you may determine 
  112. ** whether or not a cell is new by smudging a "virgin-bit" when you
  113. ** first obtain a new cell.  If you get a cell from assoc() with a
  114. ** fresh new virgin bit, then the cell must be a new one.
  115. */
  116.  
  117.  
  118. mem_cell
  119. assoc( string, table)
  120.   register char* string;
  121.   register assoc_mem table;
  122. { /* assoc */
  123.  
  124.   int length;
  125.   int hashval;
  126.  
  127.   register int   rehash;
  128.   register entry assoc_value;
  129.  
  130.   /* This is essential.  See assoc_internals.h */
  131.   if (table->entries >= table->size_div_2 - 1)
  132.     H_expand_table(table);
  133.  
  134.   hash(string, table->mask, &length, &hashval);
  135.   rehash = hashval;
  136.  
  137.  look_at_slot:
  138.   {  register entry * slot = &((*(table->array))[rehash]);
  139.  
  140.      assoc_value = *slot;
  141.  
  142.      if ( (assoc_value) == 0)
  143.        /* Nothing else has hashed to this slot. 
  144.       We will put our string here. */
  145.        { 
  146.      *slot =  cell_from_mem(
  147.             (entry) H_getmem(table->value_size + length + sizeof('\0')
  148.                      + sizeof(entry*)));
  149.  
  150.      *(mem_from_cell(*slot)) = rehash;
  151.      assoc_value = *slot;
  152.      strcpy(string_from_cell(assoc_value, table), string);
  153.      table->entries++;
  154.      return assoc_value;        /* <=========== */
  155.        }
  156.     
  157.      if (strcmp( string_from_cell(assoc_value,table), string) != 0)
  158.        {                /* Oops.. collision. */
  159.      rehash = REHASH(rehash,table);
  160.      goto look_at_slot;        /* <=========== */
  161.        }
  162.      else return assoc_value;        /* <=========== */
  163.  
  164.    }
  165.     
  166.  
  167.   
  168.  
  169.  
  170. }                    /* end assoc */
  171.  
  172. /* Like assoc, except for non-null-terminated strings */
  173. mem_cell
  174. assocn( string, length, table)
  175.   register char* string;
  176.   register assoc_mem table;
  177.   register int length;
  178. { /* assocn */
  179.  
  180.   int hashval;
  181.  
  182.   register int   rehash;
  183.   register entry assoc_value;
  184.  
  185.   /* This is essential.  See assoc_internals.h */
  186.   if (table->entries >= table->size_div_2 - 1)
  187.     H_expand_table(table);
  188.  
  189.   hashn(string, table->mask, length, &hashval);
  190.   rehash = hashval;
  191.  
  192.  look_at_slot:
  193.   {  register entry * slot = &((*(table->array))[rehash]);
  194.  
  195.      assoc_value = *slot;
  196.  
  197.      if ( (assoc_value) == 0)
  198.        /* Nothing else has hashed to this slot. 
  199.       We will put our string here. */
  200.        { 
  201.      *slot =  cell_from_mem(
  202.             (entry) H_getmem(table->value_size + length + sizeof('\0')
  203.                      + sizeof(entry*)));
  204.  
  205.      *(mem_from_cell(*slot)) = rehash;
  206.      assoc_value = *slot;
  207.      strncpy(string_from_cell(assoc_value, table), string, length);
  208.      table->entries++;
  209.      return assoc_value;        /* <=========== */
  210.        }
  211.     
  212.      if (lstrncmp( string_from_cell(assoc_value,table), string, length) != 0)
  213.        {                /* Oops.. collision. */
  214.      rehash = REHASH(rehash,table);
  215.      goto look_at_slot;        /* <=========== */
  216.        }
  217.      else return assoc_value;        /* <=========== */
  218.  
  219.    }
  220.     
  221.  
  222. }                    /* end assocn */
  223. /* Like assoc, except for non-null-terminated strings, and folds cases. */
  224. mem_cell
  225. assocnf( string, length, table)
  226.   register char* string;
  227.   register assoc_mem table;
  228.   register int length;
  229. { /* assocn */
  230.  
  231.   int hashval;
  232.  
  233.   register int   rehash;
  234.   register entry assoc_value;
  235.  
  236.   /* This is essential.  See assoc_internals.h */
  237.   if (table->entries >= table->size_div_2 - 1)
  238.     H_expand_table(table);
  239.  
  240.   hashnf(string, table->mask, length, &hashval);
  241.   rehash = hashval;
  242.  
  243.  look_at_slot:
  244.   {  register entry * slot = &((*(table->array))[rehash]);
  245.  
  246.      assoc_value = *slot;
  247.  
  248.      if ( (assoc_value) == 0)
  249.        /* Nothing else has hashed to this slot. 
  250.       We will put our string here. */
  251.        { 
  252.      *slot =  cell_from_mem(
  253.             (entry) H_getmem(table->value_size + length + sizeof('\0')
  254.                      + sizeof(entry*)));
  255.  
  256.      *(mem_from_cell(*slot)) = rehash;
  257.      assoc_value = *slot;
  258.      strncpy(string_from_cell(assoc_value, table), string, length);
  259.      table->entries++;
  260.      return assoc_value;        /* <=========== */
  261.        }
  262.     
  263.      if (lstrncmpf( string_from_cell(assoc_value,table), string, length) != 0)
  264.        {                /* Oops.. collision. */
  265.      rehash = REHASH(rehash,table);
  266.      goto look_at_slot;        /* <=========== */
  267.        }
  268.      else return assoc_value;        /* <=========== */
  269.  
  270.    }
  271.     
  272. }                    /* end assocnf */
  273.  
  274. /* returns 0 iff a null-terminated string and counted string compare */
  275. static
  276. lstrncmp(nullterm, counted, len)
  277.   char* nullterm;
  278.   char* counted;
  279. {
  280.                 
  281.   while( *nullterm && len && *nullterm++ == *counted++)
  282.     len--;
  283.   return !(len == 0 && *nullterm == 0);
  284. }
  285.  
  286. /* returns 0 iff a null-terminated string and counted string compare */
  287. /* folds cases */
  288. static
  289. lstrncmpf(nullterm, counted, len)
  290.   char* nullterm;
  291.   char* counted;
  292. {
  293.                 
  294.   while( *nullterm && len && LOWER(*nullterm) == LOWER(*counted))
  295.     {
  296.       len--;
  297.       nullterm++; counted++;
  298.     }
  299.   return !(len == 0 && *nullterm == 0);
  300. }
  301.  
  302.  
  303.  
  304. /* Look up the mem_cell associated with a given name. */
  305. /* Returns NULL if not found.                  */
  306.  
  307. mem_cell
  308. assoc_lookup( string, table)
  309.   register char* string;
  310.   register assoc_mem table;
  311. {
  312.   register entry retval;
  313.   int length;
  314.   int hashval;
  315.  
  316.   hash(string, table->mask, &length, &hashval);
  317.  
  318.   { register int  rehash = hashval;
  319.     register int * assoc_value = 0;
  320.  
  321.     look_at_slot:
  322.     {  entry * slot = &((*(table->array))[rehash]);
  323.  
  324.        assoc_value = *slot;
  325.  
  326.        if ( (assoc_value) == 0)
  327.         /* Nothing has hashed to this slot. 
  328.            Lookup has come to a sorry end. */
  329.         { 
  330.            return assoc_value;  /* <=========== */
  331.         }
  332.     
  333.        if (strcmp( string_from_cell(assoc_value,table), string) == 0)
  334.         /*  An identical string was previously put in this slot.
  335.         **  We found it!
  336.         */
  337.             { 
  338.            return assoc_value;  /* <=========== */
  339.         }
  340.  
  341.        /* collision... try next slot in the rehash orbit */
  342.        rehash = REHASH(rehash,table);
  343.        goto look_at_slot;        /* <=========== */
  344.     }
  345.  
  346.   }
  347.  
  348.  
  349. } /* end assoc_lookup */
  350.  
  351.  
  352. mem_cell
  353. assocn_lookup( string, length, table)
  354.   register char* string;
  355.   register assoc_mem table;
  356. {
  357.   register entry retval;
  358.   int hashval;
  359.  
  360.   hashn(string, table->mask, length, &hashval);
  361.   
  362.   { register int  rehash = hashval;
  363.     register int * assoc_value = 0;
  364.     
  365.   look_at_slot:
  366.     {  entry * slot = &((*(table->array))[rehash]);
  367.        
  368.        assoc_value = *slot;
  369.        
  370.        if ( (assoc_value) == 0)
  371.      /* Nothing has hashed to this slot. 
  372.         Lookup has come to a sorry end. */
  373.      { 
  374.        return assoc_value;  /* <=========== */
  375.      }
  376.        
  377.        if (lstrncmp( string_from_cell(assoc_value,table), string, length) == 0)
  378.      /*  An identical string was previously put in this slot.
  379.      **  We found it!
  380.      */
  381.      { 
  382.        return assoc_value;  /* <=========== */
  383.      }
  384.        
  385.        /* collision... try next slot in the rehash orbit */
  386.        rehash = REHASH(rehash,table);
  387.        goto look_at_slot;        /* <=========== */
  388.  
  389.      }
  390.     
  391.   }
  392.  
  393.  
  394. } /* end assocn_lookup */
  395.  
  396.  
  397. mem_cell
  398. assocnf_lookup( string, length, table)
  399.   register char* string;
  400.   register assoc_mem table;
  401. {
  402.   register entry retval;
  403.   int hashval;
  404.  
  405.   hashnf(string, table->mask, length, &hashval);
  406.   
  407.   { register int  rehash = hashval;
  408.     register int * assoc_value = 0;
  409.     
  410.   look_at_slot:
  411.     {  entry * slot = &((*(table->array))[rehash]);
  412.        
  413.        assoc_value = *slot;
  414.        
  415.        if ( (assoc_value) == 0)
  416.      /* Nothing has hashed to this slot. 
  417.         Lookup has come to a sorry end. */
  418.      { 
  419.        return assoc_value;  /* <=========== */
  420.      }
  421.        
  422.        if (lstrncmpf( string_from_cell(assoc_value,table), string, length) == 0)
  423.      /*  An identical string was previously put in this slot.
  424.      **  We found it!
  425.      */
  426.      { 
  427.        return assoc_value;  /* <=========== */
  428.      }
  429.        
  430.        /* collision... try next slot in the rehash orbit */
  431.        rehash = REHASH(rehash,table);
  432.        goto look_at_slot;        /* <=========== */
  433.  
  434.      }
  435.     
  436.   }
  437.  
  438.  
  439. } /* end assocnf_lookup */
  440.  
  441.  
  442.  
  443.  
  444. /* This routine hashes a string and counts the number of chars in it.   */
  445. /* The hash value is a function of the table size.             */
  446.  
  447. static
  448. hash(string, mask, lenp, hashp)
  449.   char* string;
  450.   int mask;     /* Is table size minus one. */
  451.   int *lenp;
  452.   int *hashp;
  453.  
  454. {
  455.   register int len = 0;
  456.   register int hash = 0;
  457.   register char* cursor = string;
  458.  
  459.   len = 0;
  460.   hash = 0;
  461.  
  462.   while (*cursor)
  463.     { len++;
  464.       hash += ((hash << 1) + *cursor++);
  465.     }
  466.   *hashp = hash & mask;
  467.   *lenp = len;  
  468.  
  469.  
  470. } /* hash */
  471.  
  472. /* Like hash, except for counted (not null-terminated) strings */
  473. static
  474. hashn(string, mask, len, hashp)
  475.   char* string;
  476.   int mask;     /* Is table size minus one. */
  477.   int len;
  478.   int *hashp;
  479.  
  480. {
  481.   register int hash = 0;
  482.   register char* cursor = string;
  483.  
  484.   hash = 0;
  485.  
  486.   while (len--)
  487.     {
  488.       hash += ((hash << 1) + *cursor++);
  489.     }
  490.   
  491.   *hashp = hash & mask;
  492.  
  493. } /* hashn*/
  494. /* Like hash, except for counted (not null-terminated) strings */
  495. /* Folds cases. */
  496. static
  497. hashnf(string, mask, len, hashp)
  498.   char* string;
  499.   int mask;     /* Is table size minus one. */
  500.   int len;
  501.   int *hashp;
  502.  
  503. {
  504.   register int hash = 0;
  505.   register char* cursor = string;
  506.  
  507.   hash = 0;
  508.  
  509.   while (len--)
  510.     {
  511.       hash += ((hash << 1) + LOWER(*cursor));
  512.       cursor++;
  513.     }
  514.   
  515.   *hashp = hash & mask;
  516.  
  517. } /* hashn*/
  518.  
  519.  
  520.  
  521.  
  522. /* "Safe" memory allocation routine... zeros out memory obtained. */
  523.  
  524. static int*
  525. H_getmem(size)
  526.   int size;
  527. {
  528.   int * retval = (int*)calloc(size, sizeof(char));
  529.   if (retval == (int*)0)
  530.     {
  531.     fprintf(PANIC_FILE, "RESOURCE FAILURE: Assoc out of memory. (%d)\n",
  532.         size);
  533.     exit(1);
  534.     }
  535.   else return retval;
  536.  
  537. }
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544. /* When a table becomes half full, we double its size.  (The
  545. ** orbit of the rehash function touches exactly half the slots.)
  546. **
  547. */
  548.  
  549. static
  550. H_expand_table(table)
  551.   register assoc_mem table;
  552. {
  553.   register hash_table * old_table = table->array;
  554.   register int old_size = table->size;
  555.  
  556.   table->size_div_2 = table->size;
  557.   table->size = table->size * 2;
  558.   table->mask = table->size -1;
  559.  
  560.   table->array = (hash_table *)H_getmem(table->size * (sizeof (mem_cell)));
  561.  
  562.   /* Move the members from the old small table to be new big one. */
  563.   { register int recno;
  564.  
  565.     for (recno = 0; recno < old_size; recno++)
  566.       if ( (*old_table)[recno] != (entry)0)
  567.         H_reassoc( (*old_table)[recno], table );
  568.   }
  569.  
  570.   cfree (old_table);
  571. }
  572.  
  573.  
  574.  
  575.  
  576.  
  577. /* This routine is a little like assoc. It is used by expand_table() to
  578. ** put entries which were in table which overflowed into the new larger one.
  579. ** Used by assoc_free() to put entries back into the table which might
  580. ** have originally been bumped down the rehash orbit by the entry being
  581. ** removed.
  582. */
  583.  
  584. static
  585. H_reassoc( rec, table)
  586.   register entry rec;
  587.   register assoc_mem table;
  588. {
  589.   register entry retval;
  590.   register char* string = string_from_cell(rec, table);
  591.   int length;
  592.   int hashval;
  593.  
  594.   hash(string, table->mask, &length, &hashval);
  595.  
  596.   { register int  rehash = hashval;
  597.  
  598.     look_at_slot:
  599.     {  register entry * slot = &((*(table->array))[rehash]);
  600.  
  601.        if ( (*slot) == 0)
  602.         { 
  603.           *slot = rec;
  604.           *(mem_from_cell(*slot)) = rehash;
  605.  
  606.           return;        /* <========= */
  607.         }
  608.     
  609.        rehash = REHASH(rehash,table);
  610.        goto look_at_slot;      /* <========= */
  611.     }
  612.     
  613.   }
  614.  
  615.  
  616. } /* H_reassoc */
  617.  
  618.  
  619.  
  620.  
  621. /* This is a sequencer for a table.  You give it a pointer to an
  622. ** integer variable which you have initialized to zero, and it gives
  623. ** you a member of the table and modifies the variable.  Call it
  624. ** again without changing the variable and it gives you the next one, etc...
  625. **
  626. **    { int handle = 0;
  627. **      mem_cell member;
  628. **
  629. **      do { member = assoc_seq(table, &handle);
  630. **           if (member != NULL) process(member);
  631. **         }
  632. **      while (member != NULL);
  633. **    }
  634. **
  635. **
  636. ** DO NOT ADD OR DELETE MEMBERS BETWEEN RELATED CALLS TO assoc_seq().
  637. ** To do so will wreak non-determinancy if the assoc() caused the
  638. ** table to expand, or if the assoc_free() caused a member to be
  639. ** moved back up the rehash chain.  You might very easily miss some
  640. ** members.
  641. **
  642. */
  643.  
  644. mem_cell
  645. assoc_seq(table, num)
  646.   register assoc_mem table;
  647.   register int *num;
  648. {
  649.   register hash_table * hash_tab = table->array;
  650.   register int size = table->size;
  651.  
  652.   /* Standard linear search algorithm looks for next non-empty slot
  653.   ** at index *num or further down.
  654.   */
  655.   for (; (*num) < size; (*num)++)
  656.     if ( (*hash_tab)[*num] != (entry)0)
  657.     { mem_cell retval = (*hash_tab)[*num];
  658.       (*num)++;
  659.       return retval;
  660.     }
  661.  
  662.   return 0;
  663.  
  664. }
  665.  
  666.  
  667.  
  668. /*
  669. ** This procedure removes a member from a table.
  670. */
  671.  
  672. assoc_free(cell, table)
  673.   mem_cell cell;
  674.   assoc_mem table;
  675. {
  676.   /* Invariant: next_slot_num and next_slot point to entries in the rehash
  677.   ** orbit of the cell being removed.  They start out pointing to the
  678.   ** slot of the condemned cell itself:
  679.   */
  680.   register next_slot_num = *(mem_from_cell(cell));
  681.   register entry *next_slot = &((*(table->array))[next_slot_num]);
  682.  
  683.   /* Remove the condemned cell. */
  684.   free(mem_from_cell(*next_slot));
  685.   *next_slot = 0; /* Splat! got it. */
  686.   table->entries -= 1;
  687.  
  688.   /* The entry we just removed might have caused some other entries
  689.   ** to be "bumped down the rehash orbit" when they were installed in
  690.   ** the table.  If that was the case, they can not now be found unless
  691.   ** they are moved to their (now) proper position in the table.
  692.   */
  693.    while(
  694.       next_slot_num = REHASH(next_slot_num,table),
  695.       next_slot = &((*(table->array))[next_slot_num]),
  696.       (*next_slot != 0)
  697.     )
  698.      { entry mover = *next_slot;
  699.        *next_slot = 0;
  700.        H_reassoc(mover, table);
  701.      }
  702.   
  703. }/* assoc_free */
  704.  
  705.  
  706.  
  707. /* Deletes a table and returns 0 if the table is empty.
  708. ** Does not delete table, but returns number of entries remaining if
  709. ** table is not empty.
  710. */
  711.  
  712. int
  713. assoc_mem_free(table)
  714.   assoc_mem table;
  715. {
  716.   if (table->entries == 0)
  717.     {  free(table->array);
  718.        free(table);
  719.        return 0;
  720.     }
  721.   else return table->entries;
  722.  
  723. }
  724.  
  725. /* Deletes all members in a table, then removes the table. */
  726.  
  727. assoc_mem_remove(table)
  728.   assoc_mem table;
  729. { register int num = 0;
  730.   register hash_table * hash_tab = table->array;
  731.   register int size = table->size;
  732.  
  733.   for (; (num) < size; (num)++)
  734.     if ( (*hash_tab)[num] != (entry)0)
  735.     { 
  736.           free(mem_from_cell((*hash_tab)[num]));
  737.     }
  738.  
  739.  
  740.   free(table->array);
  741.   free(table);
  742.  
  743. }
  744.