home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume06 / dynamich.ash < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  8.2 KB

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