home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / unix_c / languags / c / dynmchsh.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-21  |  8.0 KB  |  380 lines

  1. From riacs!eos!ames!mailrus!tut.cis.ohio-state.edu!ucbvax!decwrl!sun!pitstop!sundc!seismo!uunet!munnari!basser!msgs Thu Mar  9 08:41:15 PST 1989
  2.  
  3.  
  4. Posting-number: Volume 6, Issue 42
  5. Submitted-By: Esmond Pitt <ejp@ausmelb.oz.AU>
  6. Archive-name: dynamic-hash
  7.  
  8.  
  9. /*
  10. ** Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson.
  11. ** Coded into C, with minor code improvements, and with hsearch(3) interface,
  12. ** by ejp@ausmelb.oz, Jul 26, 1988: 13:16;
  13. ** also, hcreate/hdestroy routines added to simulate hsearch(3).
  14. **
  15. ** These routines simulate hsearch(3) and family, with the important
  16. ** difference that the hash table is dynamic - can grow indefinitely
  17. ** beyond its original size (as supplied to hcreate()).
  18. **
  19. ** Performance appears to be comparable to that of hsearch(3).
  20. ** The 'source-code' options referred to in hsearch(3)'s 'man' page
  21. ** are not implemented; otherwise functionality is identical.
  22. **
  23. ** Compilation controls:
  24. ** DEBUG controls some informative traces, mainly for debugging.
  25. ** HASH_STATISTICS causes HashAccesses and HashCollisions to be maintained;
  26. ** when combined with DEBUG, these are displayed by hdestroy().
  27. **
  28. ** Problems & fixes to ejp@ausmelb.oz. WARNING: relies on pre-processor
  29. ** concatenation property, in probably unnecessary code 'optimisation'.
  30. */
  31.  
  32. # include    <stdio.h>
  33. # include    <search.h>
  34. # include    <assert.h>
  35. # include    <string.h>
  36.  
  37. /*
  38. ** Constants
  39. */
  40.  
  41. # define SegmentSize        256
  42. # define SegmentSizeShift    8    /* log2(SegmentSize)    */
  43. # define DirectorySize        256
  44. # define DirectorySizeShift    8    /* log2(DirectorySize)    */
  45. # define Prime1            37
  46. # define Prime2            1048583
  47. # define DefaultMaxLoadFactor    5
  48.  
  49. /*
  50. ** Fast arithmetic, relying on powers of 2,
  51. ** and on pre-processor concatenation property
  52. */
  53.  
  54. # define MUL(x,y)        ((x) << (y/**/Shift))
  55. # define DIV(x,y)        ((x) >> (y/**/Shift))
  56. # define MOD(x,y)        ((x) & ((y)-1))
  57.  
  58. /*
  59. ** local data templates
  60. */
  61.  
  62. typedef struct element
  63.     {
  64.     /*
  65.     ** The user only sees the first two fields,
  66.     ** as we pretend to pass back only a pointer to ENTRY.
  67.     ** {S}he doesn't know what else is in here.
  68.     */
  69.     char        *Key;
  70.     char        *Data;
  71.     struct element    *Next;    /* secret from user    */
  72.     } Element,*Segment;
  73.  
  74. typedef struct
  75.     {
  76.     short    p;        /* Next bucket to be split    */
  77.     short    maxp;        /* upper bound on p during expansion    */
  78.     long    KeyCount;    /* current # keys    */
  79.     short    SegmentCount;    /* current # segments    */
  80.     short    MinLoadFactor;
  81.     short    MaxLoadFactor;
  82.     Segment    *Directory[DirectorySize];
  83.     } HashTable;
  84.  
  85. typedef unsigned long    Address;
  86.  
  87. /*
  88. ** external routines
  89. */
  90.  
  91. extern char    *calloc();
  92. extern int    free();
  93.  
  94. /*
  95. ** Entry points
  96. */
  97.  
  98. extern int    hcreate();
  99. extern void    hdestroy();
  100. extern ENTRY    *hsearch();
  101.  
  102. /*
  103. ** Internal routines
  104. */
  105.  
  106. static Address    Hash();
  107. static void    ExpandTable();
  108.  
  109. /*
  110. ** Local data
  111. */
  112.  
  113. static HashTable    *Table = NULL;
  114. # if HASH_STATISTICS
  115. static long        HashAccesses, HashCollisions;
  116. # endif
  117.  
  118. /*
  119. ** Code
  120. */
  121.  
  122. int
  123. hcreate(Count)
  124. unsigned Count;
  125. {
  126.     int        i;
  127.  
  128.     /*
  129.     ** Adjust Count to be nearest higher power of 2,
  130.     ** minimum SegmentSize, then convert into segments.
  131.     */
  132.     i = SegmentSize;
  133.     while (i < Count)
  134.     i <<= 1;
  135.     Count = DIV(i,SegmentSize);
  136.  
  137.     Table = (HashTable*)calloc((sizeof(HashTable),1));
  138.     if (Table == NULL)
  139.     return(0);
  140.     /*
  141.     ** resets are redundant - done by calloc(3)
  142.     **
  143.     Table->SegmentCount = Table->p = Table->KeyCount = 0;
  144.     */
  145.     /*
  146.     ** Allocate initial 'i' segments of buckets
  147.     */
  148.     for (i = 0; i < Count; i++)
  149.     {
  150.     Table->Directory[i] = (Segment*)calloc(sizeof(Segment),SegmentSize);
  151.     if (Table->Directory[i] == NULL)
  152.     {
  153.         hdestroy();
  154.         return(0);
  155.     }
  156.     Table->SegmentCount++;
  157.     }
  158.     Table->maxp = MUL(Count,SegmentSize);
  159.     Table->MinLoadFactor = 1;
  160.     Table->MaxLoadFactor = DefaultMaxLoadFactor;
  161. # if DEBUG
  162.     fprintf(
  163.         stderr,
  164.         "[hcreate] Table %x Count %d maxp %d SegmentCount %d\n",
  165.         Table,
  166.         Count,
  167.         Table->maxp,
  168.         Table->SegmentCount
  169.         );
  170. # endif
  171. # if HASH_STATISTICS
  172.     HashAccesses = HashCollisions = 0;
  173. # endif
  174.     return(1);
  175. }
  176.  
  177. void
  178. hdestroy()
  179. {
  180.     int        i,j;
  181.     Segment    *s;
  182.     Element    *p,*q;
  183.  
  184.     if (Table != NULL)
  185.     {
  186.     for (i = 0; i < Table->SegmentCount; i++)
  187.     {
  188.         /* test probably unnecessary    */
  189.         if ((s = Table->Directory[i]) != NULL)
  190.         {
  191.         for (j = 0; j < SegmentSize; j++)
  192.         {
  193.             p = s[j];
  194.             while (p != NULL)
  195.             {
  196.             q = p->Next;
  197.             free((char*)p);
  198.             p = q;
  199.             }
  200.         free(Table->Directory[i]);
  201.         }
  202.         }
  203.     }
  204.     free(Table);
  205.     Table = NULL;
  206. # if HASH_STATISTICS && DEBUG
  207.     fprintf(
  208.         stderr,
  209.         "[hdestroy] Accesses %ld Collisions %ld\n",
  210.         HashAccesses,
  211.         HashCollisions
  212.         );
  213. # endif
  214.     }
  215. }
  216.  
  217. ENTRY *
  218. hsearch(item,action)
  219. ENTRY   item;
  220. ACTION       action;    /* FIND/ENTER    */
  221. {
  222.     Address    h;
  223.     Segment    *CurrentSegment;
  224.     int        SegmentIndex;
  225.     int        SegmentDir;
  226.     Segment    *p,q;
  227.  
  228.     assert(Table != NULL);    /* Kinder really than return(NULL);    */
  229. # if HASH_STATISTICS
  230.     HashAccesses++;
  231. # endif
  232.     h = Hash(item.key);
  233.     SegmentDir = DIV(h,SegmentSize);
  234.     SegmentIndex = MOD(h,SegmentSize);
  235.     /*
  236.     ** valid segment ensured by Hash()
  237.     */
  238.     CurrentSegment = Table->Directory[SegmentDir];
  239.     assert(CurrentSegment != NULL);    /* bad failure if tripped    */
  240.     p = &CurrentSegment[SegmentIndex];
  241.     q = *p;
  242.     /*
  243.     ** Follow collision chain
  244.     */
  245.     while (q != NULL && strcmp(q->Key,item.key))
  246.     {
  247.     p = &q->Next;
  248.     q = *p;
  249. # if HASH_STATISTICS
  250.     HashCollisions++;
  251. # endif
  252.     }
  253.     if (
  254.     q != NULL    /* found    */
  255.     ||
  256.     action == FIND    /* not found, search only    */
  257.     ||
  258.     (q = (Element*)calloc(sizeof(Element),1))
  259.     ==
  260.     NULL        /* not found, no room    */
  261.     )
  262.     return((ENTRY*)q);
  263.     *p = q;            /* link into chain    */
  264.     /*
  265.     ** Initialize new element
  266.     */
  267.     q->Key = item.key;
  268.     q->Data = item.data;
  269.     /*
  270.     ** cleared by calloc(3)
  271.     q->Next = NULL;
  272.     */
  273.     /*
  274.     ** Table over-full?
  275.     */
  276.     if (++Table->KeyCount / MUL(Table->SegmentCount,SegmentSize) > Table->MaxLoadFactor)
  277.     ExpandTable();    /* doesn't affect q    */
  278.     return((ENTRY*)q);
  279. }
  280.  
  281. /*
  282. ** Internal routines
  283. */
  284.  
  285. static Address
  286. Hash(Key)
  287. char *Key;
  288. {
  289.     Address        h,address;
  290.     register unsigned char    *k = (unsigned char*)Key;
  291.  
  292.     h = 0;
  293.     /*
  294.     ** Convert string to integer
  295.     */
  296.     while (*k)
  297.     h = h*Prime1 ^ (*k++ - ' ');
  298.     h %= Prime2;
  299.     address = MOD(h,Table->maxp);
  300.     if (address < Table->p)
  301.     address = MOD(h,(Table->maxp << 1));    /* h % (2*Table->maxp)    */
  302.     return(address);
  303. }
  304.  
  305. void
  306. ExpandTable()
  307. {
  308.     Address    NewAddress;
  309.     int        OldSegmentIndex,NewSegmentIndex;
  310.     int        OldSegmentDir,NewSegmentDir;
  311.     Segment    *OldSegment,*NewSegment;
  312.     Element    *Current,**Previous,**LastOfNew;
  313.  
  314.     if (Table->maxp + Table->p < MUL(DirectorySize,SegmentSize))
  315.     {
  316.     /*
  317.     ** Locate the bucket to be split
  318.     */
  319.     OldSegmentDir = DIV(Table->p,SegmentSize);
  320.     OldSegment = Table->Directory[OldSegmentDir];
  321.     OldSegmentIndex = MOD(Table->p,SegmentSize);
  322.     /*
  323.     ** Expand address space; if necessary create a new segment
  324.     */
  325.     NewAddress = Table->maxp + Table->p;
  326.     NewSegmentDir = DIV(NewAddress,SegmentSize);
  327.     NewSegmentIndex = MOD(NewAddress,SegmentSize);
  328.     if (NewSegmentIndex == 0)
  329.         Table->Directory[NewSegmentDir] = (Segment*)calloc(sizeof(Segment),SegmentSize);
  330.     NewSegment = Table->Directory[NewSegmentDir];
  331.     /*
  332.     ** Adjust state variables
  333.     */
  334.     Table->p++;
  335.     if (Table->p == Table->maxp)
  336.     {
  337.         Table->maxp <<= 1;    /* Table->maxp *= 2    */
  338.         Table->p = 0;
  339.     }
  340.     Table->SegmentCount++;
  341.     /*
  342.     ** Relocate records to the new bucket
  343.     */
  344.     Previous = &OldSegment[OldSegmentIndex];
  345.     Current = *Previous;
  346.     LastOfNew = &NewSegment[NewSegmentIndex];
  347.     *LastOfNew = NULL;
  348.     while (Current != NULL)
  349.     {
  350.         if (Hash(Current->Key) == NewAddress)
  351.         {
  352.         /*
  353.         ** Attach it to the end of the new chain
  354.         */
  355.         *LastOfNew = Current;
  356.         /*
  357.         ** Remove it from old chain
  358.         */
  359.         *Previous = Current->Next;
  360.         LastOfNew = &Current->Next;
  361.         Current = Current->Next;
  362.         *LastOfNew = NULL;
  363.         }
  364.         else
  365.         {
  366.         /*
  367.         ** leave it on the old chain
  368.         */
  369.         Previous = &Current->Next;
  370.         Current = Current->Next;
  371.         }
  372.     }
  373.     }
  374. }
  375. -- 
  376. Esmond Pitt, Austec (Asia/Pacific) Ltd
  377. ...!uunet.UU.NET!munnari!ausmelb!ejp,ejp@ausmelb.oz
  378.  
  379.  
  380.