home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume6 / dynamic-hash < prev    next >
Internet Message Format  |  1991-03-28  |  8KB

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