home *** CD-ROM | disk | FTP | other *** search
/ Internet Publisher's Toolbox 1.0 / Image.iso / toolbox / ntserver / wtsource / hash.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-04  |  16.7 KB  |  568 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4. */
  5.  
  6. /* Copyright (c) CNIDR (see ../COPYRIGHT) */
  7.  
  8.  
  9. /* Hash Table routines
  10.  * for strings based on Common Lisp interface functions.
  11.  *  -brewster
  12.  */
  13.  
  14. /* This hashtable will never grow, rather it just adds entries to the buckets.
  15.  * 
  16.  * The contents are all in one contiguous block so that the contents can be 
  17.  * sorted.  This will cause problems because of the reallocs needed.
  18.  *
  19.  */
  20.  
  21. /* Change log:
  22.  * $Log: hash.c,v $
  23.  * Revision 1.1  1993/02/16  15:05:35  freewais
  24.  * Initial revision
  25.  *
  26.  * Revision 1.9  92/05/06  17:27:38  jonathan
  27.  * Added cast for htable->contents malloc.
  28.  * 
  29.  * Revision 1.8  92/02/21  11:06:53  jonathan
  30.  * Added header, RCSIdent.
  31.  * 
  32.  * Revision 1.7  92/02/16  12:37:57  jonathan
  33.  * Changed bzero's to memset's.
  34.  * 
  35.  * Revision 1.6  92/02/12  13:20:08  jonathan
  36.  * Added "$Log" so RCS will put the log message in the header
  37.  * 
  38. */
  39.  
  40. /* Cool things we could do from here:
  41.      A) Use a signature to see if the word is in the hashtable, so that
  42. get_hash will return NULL very quickly for the new words.  This will reduce
  43. the number of times a whole bucket must be searched.  This might not help
  44. much since the file that I was indexing had 8M words, and only 350k
  45. different words, so there are 1 NULL in 30 requests expected. Therefore,
  46. unless searching down a list of buckets takes a long time, this is not a
  47. big problem.  The only case I can think of is if it induces paging.
  48.     B) Since the most recently accessed entry of the hashtable is at the
  49. front, we can sort the hashtable entries so that the heads of all the
  50. buckets are clumped, so that hopefully these will stay in memory, and the
  51. infrequently used ones would move to the paged out space.
  52. */
  53.  
  54. #ifndef lint
  55. static char *RCSid = "$Header: /usr/users/freewais/FreeWAIS-0.1/ir/hash.c,v 1.1 1993/02/16 15:05:35 freewais Exp $";
  56. #endif
  57.  
  58. #include "cdialect.h"
  59. #include "cutil.h"
  60. #include "hash.h"
  61. #include <string.h>
  62. #include "panic.h"
  63.  
  64. #define ALIGNMENT_SIZE 4 /* the word size to align elements of the contents on */
  65.  
  66.  
  67. hashtable*
  68. make_hashtable (number_of_buckets, number_of_elements, element_size)
  69.      long number_of_buckets;
  70.      long number_of_elements;
  71.      long element_size;
  72. {
  73.   hashtable *htable = (hashtable *)s_malloc(sizeof(hashtable));
  74.  
  75.   if(number_of_buckets > 0)
  76.     htable->number_of_buckets = number_of_buckets;
  77.   else
  78.     htable->number_of_buckets = DEFAULT_NUMBER_OF_BUCKETS;
  79.  
  80.   htable->buckets_mask = htable->number_of_buckets - 1;
  81.  
  82.   if(element_size > 0){
  83.     htable->element_size = element_size;
  84.   }
  85.   else
  86.     return(NULL);
  87.   
  88.   htable->number_of_elements = 0;
  89.  
  90.   if(number_of_elements > 0)
  91.     htable->max_number_of_elements = number_of_elements;
  92.   else
  93.     htable->max_number_of_elements = DEFAULT_NUMBER_OF_ELEMENTS;
  94.  
  95.   htable->buckets
  96.     = (long*)s_malloc(sizeof(long) * htable->number_of_buckets);
  97.   /* this depends on -1L to be made up of -1 characters */
  98.   memset(htable->buckets, -1, sizeof(long) * htable->number_of_buckets);
  99.  
  100.   htable->contents 
  101.     = (hash_entry*)s_malloc(htable->max_number_of_elements * 
  102.                 htable->element_size);
  103.   return(htable);
  104. }
  105.  
  106.  
  107. /*----------------------------------------------------------------- */
  108.  
  109. /* returns a hashcode for a string.  
  110.  * key is a string that is assumed to be downcased (if appropriate),
  111.  * function is a number from 0 to 20 that are fairly independent hash 
  112.  *    functions.
  113.  */
  114.  
  115.  
  116. /*===========================*
  117.  *===  Hashing Functions  ===*
  118.  *===========================*/
  119.  
  120.  
  121. /*___________________________________________________________________________*/
  122.  
  123. /* Date: Fri, 13 Dec 91 16:28:53 EST
  124.    From: Henry Massalin <henry@cs.columbia.edu> */
  125.  
  126. #define reg    register
  127.  
  128. hash(s)
  129. reg    unsigned char    *s;
  130. {
  131.   reg    int        c;
  132.   reg    unsigned long    h = 0;
  133.   extern    unsigned long    RandHash[];
  134.  
  135.   if((h = (unsigned long)*s++) == 0)
  136.     return(h);
  137.   while((c = *s++) != 0)
  138.     h += RandHash[ 0xFF & ((h >> 24) ^ c )];
  139.   return(h);
  140. }
  141.  
  142. /*___________________________________________________________________________*/
  143.  
  144. /*
  145.  * Almost any random permutation will work.  Of course, now that I've
  146.  * picked this one, everyone else must use it also, to be consistent.
  147.  *
  148.  * This array is constructed from four independent permutations of the
  149.  * integers 0..255.  A different permutation in each of the bytes.
  150.  */
  151. unsigned long
  152.     RandHash[256] = {
  153.     0x4BEF74B7,0x7FE1F5A6,0xB4C08BF6,0xC2CF0CD0,
  154.     0x788E2A29,0xB043B6B9,0x5B56C90C,0x324D8FE7,
  155.     0x8D9C854A,0xCAE06AC8,0x902076A1,0x949EAA31,
  156.     0xDC070A08,0xF7499B6D,0x17883EAA,0x22A65A35,
  157.     0x34FC6407,0x8322F367,0xD105BB0F,0xD8A29526,
  158.     0x8A1B708A,0xBE85B1E1,0xC66AC334,0xC1B13A1C,
  159.     0xEDF644E4,0x3EA3806C,0x2732D31D,0xEBF504A9,
  160.     0x08BB2B52,0x4775992C,0x3C0B8CF7,0x5FA0AEA2,
  161.     0x7616D761,0x66AA86DB,0xC73305C0,0x09E6EA99,
  162.     0xC9789DD6,0xF47340F1,0x3941303E,0x7542233A,
  163.     0xE0812ED1,0xFF2E54FE,0x07B887EB,0x68CD0E93,
  164.     0xB124C17F,0xD01A9C5B,0x96B490ED,0xA7CAE5FC,
  165.     0x18D41F37,0x6E8DA1BC,0x9B631940,0x35ABCFD4,
  166.     0xACA1FB0A,0x2F71B921,0x6F10A747,0xA6680796,
  167.     0x01F12271,0xFDBE0378,0x02F0FF79,0xEC3FF2D2,
  168.     0x84485C9C,0x60BF9113,0x98CC35E3,0xAA2A8D4D,
  169.     0xA5CBA2E9,0xF0AE1EF2,0x990118E2,0x64A966F5,
  170.     0x8595490D,0x7E9B4391,0xE8311B7D,0x2474BCBF,
  171.     0x19D6E286,0xDFD32D5C,0xAB5116CA,0x7D72D6D5,
  172.     0x0BF44F5E,0x6170B3D3,0x2C471CC2,0x2EFD1118,
  173.     0x8C878836,0x62FE52E0,0x2B8C1277,0x2ACEBDEC,
  174.     0xB2E8A8A3,0x30B757AE,0x708BBA8D,0x501EF654,
  175.     0x49A7EF1E,0xFE7636AD,0xB54BBE4F,0x1ADBDB8C,
  176.     0x72E24DD9,0xF68A2517,0xF56FEEDF,0x5A7EB75D,
  177.     0xD606F9E6,0xC32526B8,0x5EB613F3,0x0C08CBDE,
  178.     0x8B965E59,0xB361EB75,0x3FADE465,0xB8FFD16F,
  179.     0x26442988,0x00D2D830,0x3D58FE41,0x3B386E44,
  180.     0x1297E914,0xAFC11083,0xC5EBE8B6,0x15A49ECD,
  181.     0xF950C263,0xA0DA0982,0x4CBDF101,0xE227F77E,
  182.     0x556DB8DA,0xA1BA2125,0x1E3BD409,0xFCF25155,
  183.     0x110A983B,0x7B7F1A4C,0xE7FB4C69,0xC8994E1F,
  184.     0x586C4A42,0x5C1FAF2F,0xAD777D48,0x9186696B,
  185.     0xD52181B4,0xE94C39EF,0x9EDEB2FF,0x802FDA20,
  186.     0xA228D902,0x52BC7CFD,0x53DD47AC,0x6D12D25F,
  187.     0xCE1D6D8F,0xF12C8E2B,0xDD5700C1,0x9A402851,
  188.     0x386E53D7,0x0DF88281,0x4A55CDC4,0x653692B0,
  189.     0x203CAC8E,0x747C6164,0xC47A02B2,0x1B4FC82D,
  190.     0xF8934B7A,0x36A82CB5,0x690CD080,0x2800A524,
  191.     0x1C045D0E,0xBF09CAF0,0x8814ABB3,0xE3D0776A,
  192.     0x219AA6D8,0xCC375653,0xFA2327A8,0x57F75F27,
  193.     0x732DA903,0x8F696738,0x3A453223,0xDA1C9F32,
  194.     0xCF90152A,0x0F64BFF4,0xD7533FA5,0x8E7D55DC,
  195.     0xBC598998,0x45110DBA,0x4D92E6FB,0x7746C01A,
  196.     0x951348A0,0xBBFA7E9B,0x055EAD7B,0x06B36BBB,
  197.     0x0EE31D97,0x37297F11,0xEF529450,0x51C6E168,
  198.     0x1F67C63F,0x6B023443,0x449F5BFA,0x48C39A4B,
  199.     0xD30E426E,0x0ADFE31B,0x467B6085,0x7CC49676,
  200.     0x140DFA60,0xE16B7274,0x235AEC05,0xD2D7DCC6,
  201.     0x13946212,0x339D2462,0xFB82F8EE,0xB6197970,
  202.     0xBD0F3C9D,0xE64E3B28,0xE4B07A87,0x54C92F95,
  203.     0x6CC583AB,0xD4182015,0x86DC755A,0xA930A333,
  204.     0x82B273EA,0x923EED7C,0xF354A02E,0xEEEEA439,
  205.     0x1D89089A,0xD9EDDEE8,0x1017B504,0xA45CB0C7,
  206.     0x93C7E0A4,0x43D95858,0xDB654594,0x31D578CF,
  207.     0xCDF98489,0xB915C7F9,0x67846FE5,0x04D150AF,
  208.     0x5DF314DD,0xCB911772,0xA8627156,0x258FC573,
  209.     0xEAA5E79F,0x633D4119,0x793501C9,0x7AD80F0B,
  210.     0x4E5DF04E,0x97AF6884,0xAE3A9745,0xA3ACF48B,
  211.     0x16398A10,0xDE8346C3,0x42C20606,0x03EAB4B1,
  212.     0x8798FDCC,0x9CE4FC22,0x81B5DD3D,0x563431BE,
  213.     0x2D799349,0xF2036C92,0xB7E97B3C,0x71E559C5,
  214.     0x5960C4BD,0x295B63F8,0xBA5F0B57,0xE5B93DCE,
  215.     0x6A4ACC90,0x89EC6516,0x4180D566,0x40263300,
  216.     0x9DE738CB,0x4F2B3746,0x9F66DF9E,0xC0C8CEA7
  217. };
  218.  
  219. /*___________________________________________________________________________*/
  220.  
  221. /*
  222.  * Create permutations
  223.  */
  224. #if 0
  225. #define N        256
  226. #define    ulong        unsigned long
  227.  
  228. main()
  229. {
  230.     int     i, j;
  231.     ulong    p[N], q[N];
  232.  
  233.     permute(p);
  234.     for(i=3; --i >= 0; ) {
  235.         permute(q);
  236.         for(j=N; --j >= 0; )
  237.             p[j] = (p[j] << 8) + q[j];
  238.     }
  239.  
  240.     printf("ulong    RandHash[%d] = {", N);
  241.     for(i=0; i<N; i++)
  242.         printf("%s0x%08X", ((i&3) ? "," : "\n\t"), p[i]);
  243.     printf("\n};\n\n");
  244. }
  245.  
  246. permute(p)
  247. ulong    *p;
  248. {
  249.     int     i, r, t;
  250.  
  251.     /* initialize array */
  252.     for(i=N; --i >= 0; )
  253.         p[i] = i;
  254.  
  255.     /* generate random permutation */
  256.     for(i=N; --i >= 0; ) {
  257.         r = (i * (random() & 0xFFFF)) >> 16;
  258.         t = p[r]; p[r] = p[i]; p[i] = t;
  259.     }
  260. #if 0
  261.     /* make sure there are no cycles of len < N */
  262.     for(i=N; --i >= 0; ) {
  263.     }
  264. #endif
  265. }
  266.  
  267. #endif
  268.  
  269. /* --------------------------- */
  270.  
  271. #ifdef UNUSED
  272.  
  273. /* courtesy ses@ccgr.technion.ac.il, but it turns out in
  274.    informal timings that it increases the index time.  sigh. */
  275.  
  276. static char coeff[] = {
  277.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
  278.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
  279.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
  280.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
  281.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
  282.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
  283.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
  284.         61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1};
  285.  
  286.  
  287. long hash_code(key, function)
  288.      char *key;
  289.      long function;
  290. {
  291.   register char *foo;
  292.   register long hash = 0;
  293.   register int l;
  294.  
  295.   for(l=function,foo=key;l<sizeof(coeff) && *foo ;l++)
  296.     hash = hash + (*(foo++) * coeff[l]);
  297.  
  298.   return(hash);
  299. }
  300.  
  301. #endif /* def UNUSED   */
  302.  
  303.  
  304. /*----------------------------------------------------------------- */
  305.  
  306. #define HASHTABLE_GROW_FACTOR 4
  307.  
  308. hash_entry *put_hash(key, htable, element)
  309.      char *key;
  310.      hashtable *htable;
  311.      hash_entry *element;
  312. {
  313.   /* allocate the entry out of the contents area, then add the entry to 
  314.      the head of the bucket list. */
  315.  
  316.   /* old long bucket_number = hash_code(key, 0) & htable->buckets_mask; */
  317.   long bucket_number = hash((unsigned char*)key) & htable->buckets_mask;
  318.   long old_bucket_value =  (htable->buckets)[bucket_number];
  319.   long new_bucket_value = htable->number_of_elements++;
  320.   hash_entry *entry_header;
  321.  
  322.   if(new_bucket_value >= htable->max_number_of_elements){
  323.     long new_size = 
  324.       HASHTABLE_GROW_FACTOR * htable->max_number_of_elements * 
  325.     htable->element_size;
  326.     
  327.     waislog(WLOG_LOW, WLOG_INFO, 
  328.         "Expanding hashtable from %ld bytes to %ld bytes address %ld...",
  329.        htable->max_number_of_elements * htable->element_size ,
  330.        new_size, htable->contents);
  331.     htable->max_number_of_elements *= HASHTABLE_GROW_FACTOR;
  332.     htable->contents = (hash_entry*)s_realloc(htable->contents, new_size);
  333.     if(NULL == htable->contents)
  334.       panic("Out of virtual memory.");
  335.   }
  336.   entry_header = &(htable->contents)[new_bucket_value];
  337.   memcpy((char *)entry_header, (char *)element, htable->element_size);
  338.   /* printf("Writing key: '%s' into index %ld\n", key, new_bucket_value); */
  339.   strncpy(entry_header->key, key, MAX_KEY_SIZE + 1); 
  340.   entry_header->next = old_bucket_value;
  341.  
  342.   (htable->buckets)[bucket_number] = new_bucket_value;
  343.   return(entry_header);
  344. }
  345.  
  346. /*----------------------------------------------------------------- */
  347.  
  348. /* returns a pointer to the element stored or NULL if none. */
  349. hash_entry *get_hash (key, htable)
  350.      char *key;
  351.      hashtable *htable;
  352. {
  353.   /* this looks up the value in the bucket and puts it at the head of the 
  354.      bucket. */
  355.  
  356.   /* old long bucket_number = hash_code(key, 0) & htable->buckets_mask; */
  357.   long bucket_number = hash((unsigned char*)key) & htable->buckets_mask;
  358.   long old_bucket_value =  (htable->buckets)[bucket_number];
  359.  
  360.   long next_contents_index = old_bucket_value;
  361.  
  362.   while(next_contents_index != -1){
  363.     /* keep looking down the list for the right value */
  364.     if(0 == strcmp((htable->contents)[next_contents_index].key, 
  365.            key))
  366.       /* found it */
  367.       return(&(htable->contents)[next_contents_index]);
  368.     else
  369.       next_contents_index = (htable->contents)[next_contents_index].next;
  370.   }
  371.   return(NULL);
  372. }
  373.   
  374.  
  375. /*----------------------------------------------------------------- */
  376.  
  377. boolean clr_hashtable (htable)
  378.      hashtable *htable;
  379. {    
  380.   htable->number_of_elements = 0;
  381.   memset(htable->buckets, -1, sizeof(long) * htable->number_of_buckets);
  382.   return(true);
  383. }
  384.  
  385. /*----------------------------------------------------------------- */
  386.  
  387. /* removes contents and free's memory for the hastable.   
  388.    returns true if successful, false otherwise */
  389. boolean free_hashtable(htable)
  390.      hashtable *htable;
  391. {
  392.   s_free(htable->contents);
  393.   s_free(htable->buckets);
  394.   s_free(htable);
  395.   return(true);
  396. }
  397.  
  398. /*----------------------------------------------------------------- */
  399.  
  400. /* returns the number of elements in the hashtable */
  401. long hashtable_count(htable)
  402.      hashtable *htable;
  403. {
  404.   return(htable->number_of_elements);
  405. }
  406.  
  407. /*----------------------------------------------------------------- */
  408.  
  409. #ifdef WIN32
  410. static int hash_entry_compare _AP((const void *i,const void *j));
  411.  
  412. static int hash_entry_compare(const void *I,const void *J)
  413. {
  414.   hash_entry *i, *j;
  415.   i = (hash_entry *) I;
  416.   j = (hash_entry *) J;
  417. #else
  418. static int hash_entry_compare _AP((hash_entry *i,hash_entry *j));
  419.  
  420. static int hash_entry_compare(i,j)
  421. hash_entry *i;
  422. hash_entry *j;
  423. {
  424. #endif
  425.   return(strcmp(i->key, j->key));
  426. }
  427.  
  428.  
  429. boolean sort_hashtable(htable)
  430.      hashtable *htable;
  431. {
  432. #ifdef WIN32
  433.   qsort((void *)htable->contents,
  434. #else
  435.   qsort(htable->contents,
  436. #endif
  437.     htable->number_of_elements,
  438.     (size_t)sizeof(hash_entry),
  439.     hash_entry_compare);
  440.   return(true);
  441. }
  442.  
  443. /*----------------------------------------------------------------- */
  444. /* print routines */
  445.  
  446.  
  447. static void print_content_element(index, htable)
  448.      long index;
  449.      hashtable *htable;
  450. {  
  451.   printf("  Index: %ld, key: '%s', Next index %ld\n", 
  452.      index, (htable->contents[index]).key, 
  453.      (htable->contents[index]).next);
  454. }
  455.  
  456. void print_bucket(bucket, htable)
  457.      long bucket;
  458.      hashtable *htable;
  459. {
  460.   long next_contents_index = (htable->buckets)[bucket];
  461.  
  462.   printf(" Bucket: %ld\n", bucket);
  463.   while(next_contents_index != -1){
  464.     hash_entry * entry_header = &(htable->contents)[next_contents_index];
  465.     print_content_element(next_contents_index, htable);
  466.     next_contents_index = entry_header->next;
  467.   }
  468.   return;
  469. }
  470.  
  471. long bucket_length(bucket, htable)
  472.      long bucket;
  473.      hashtable *htable;
  474. {
  475.   long next_contents_index = (htable->buckets)[bucket];
  476.   long answer = 0;
  477.   while(next_contents_index != -1){
  478.     answer++;
  479.     next_contents_index = ((htable->contents)[next_contents_index]).next;
  480.   }
  481.   return(answer);
  482. }
  483.  
  484. static int bucket_length_compare _AP((long*i,long* j));
  485.  
  486. static int bucket_length_compare(i,j)
  487. long *i;
  488. long *j;
  489. {
  490.   return((*j) - (*i));
  491. }
  492.  
  493. void analyze_hashtable_distribution(htable)
  494.      hashtable * htable;
  495. {
  496.   long count = 0;
  497.   long max_length = 128;
  498.   long *bucket_length_array = (long *)malloc(sizeof(long) * max_length);
  499.   memset((char*)bucket_length_array, 0, (sizeof(long) * max_length));
  500.  
  501.   waislog(WLOG_LOW, WLOG_INFO, "--Hashtable Analysis--");
  502.   waislog(WLOG_LOW, WLOG_INFO, "Number of buckets: %ld",
  503.       htable->number_of_buckets);
  504.   waislog(WLOG_LOW, WLOG_INFO, "Number of allocated elements %ld",
  505.       htable->number_of_elements);
  506.   waislog(WLOG_LOW, WLOG_INFO, "Max number of elements %ld",
  507.       htable->max_number_of_elements);
  508.  
  509.   while(count < htable->number_of_buckets){
  510.     long length = bucket_length(count, htable);
  511.     if(length > max_length){  /* too small, start again */
  512.       max_length *= 2;
  513.       free((char*)bucket_length_array);
  514.       bucket_length_array = (long *)malloc(sizeof(long) * max_length);
  515.       memset((char*)bucket_length_array, 0, (sizeof(long) * max_length));
  516.       count = 0;
  517.     }
  518.     else{
  519.       bucket_length_array[length]++;
  520.       count++;
  521.     }
  522.   }
  523.   /* print the buckets */
  524.   for(count = 0; count < max_length; count++){
  525.     if(bucket_length_array[count] > 0){
  526.       waislog(WLOG_LOW, WLOG_INFO, "Length %4ld, number of buckets: %5ld.",
  527.           count, bucket_length_array[count]);
  528.     }
  529.   }
  530.   free((char*)bucket_length_array);
  531. }
  532.  
  533. void print_hashtable(htable)
  534.      hashtable * htable;
  535. {
  536.   long count;
  537.   printf("Number of buckets: %ld\n",  htable->number_of_buckets);
  538.   printf("Bucket mask %ld\n", htable->buckets_mask);
  539.   printf("Element Size %ld\n", htable->element_size);
  540.   printf("Number of allocated elements %ld\n", htable->number_of_elements);
  541.   printf("Max number of elements %ld\n", htable->max_number_of_elements);
  542.   
  543.   printf("\nContents:\n");
  544.   for(count = 0; count < htable->number_of_elements; count++){
  545.     print_content_element(count, htable);    
  546.   }
  547.   printf("\nBuckets:\n");
  548.   for(count = 0; count < htable->number_of_buckets; count++){
  549.     if((htable->buckets)[count] != -1)
  550.       print_bucket(count, htable);    
  551.   }
  552.  
  553. /* for saber trials:
  554.  
  555. foo(){
  556. hashtable *htable;
  557. hash_entry entry;
  558. htable = make_hashtable(0, 0, sizeof(hash_entry));
  559. print_hashtable(htable);
  560. put_hash("food", htable, &entry);
  561. print_hashtable(htable);
  562. *get_hash("food", htable);
  563. put_hash("food", htable, &entry);
  564. print_hashtable(htable);
  565. }
  566. */
  567.