hash.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:16k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    
  3.    This library is free software; you can redistribute it and/or
  4.    modify it under the terms of the GNU Library General Public
  5.    License as published by the Free Software Foundation; either
  6.    version 2 of the License, or (at your option) any later version.
  7.    
  8.    This library is distributed in the hope that it will be useful,
  9.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11.    Library General Public License for more details.
  12.    
  13.    You should have received a copy of the GNU Library General Public
  14.    License along with this library; if not, write to the Free
  15.    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  16.    MA 02111-1307, USA */
  17. /* The hash functions used for saveing keys */
  18. /* One of key_length or key_length_offset must be given */
  19. /* Key length of 0 isn't allowed */
  20. #include "mysys_priv.h"
  21. #include <m_string.h>
  22. #include <m_ctype.h>
  23. #include "hash.h"
  24. #define NO_RECORD ((uint) -1)
  25. #define LOWFIND 1
  26. #define LOWUSED 2
  27. #define HIGHFIND 4
  28. #define HIGHUSED 8
  29. static uint hash_mask(uint hashnr,uint buffmax,uint maxlength);
  30. static void movelink(HASH_LINK *array,uint pos,uint next_link,uint newlink);
  31. static uint calc_hashnr(const byte *key,uint length);
  32. static uint calc_hashnr_caseup(const byte *key,uint length);
  33. static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length);
  34. my_bool hash_init(HASH *hash,uint size,uint key_offset,uint key_length,
  35.   hash_get_key get_key,
  36.   void (*free_element)(void*),uint flags)
  37. {
  38.   DBUG_ENTER("hash_init");
  39.   DBUG_PRINT("enter",("hash: %lx  size: %d",hash,size));
  40.   hash->records=0;
  41.   if (init_dynamic_array(&hash->array,sizeof(HASH_LINK),size,0))
  42.   {
  43.     hash->free=0; /* Allow call to hash_free */
  44.     DBUG_RETURN(TRUE);
  45.   }
  46.   hash->key_offset=key_offset;
  47.   hash->key_length=key_length;
  48.   hash->blength=1;
  49.   hash->current_record= NO_RECORD; /* For the future */
  50.   hash->get_key=get_key;
  51.   hash->free=free_element;
  52.   hash->flags=flags;
  53.   if (flags & HASH_CASE_INSENSITIVE)
  54.     hash->calc_hashnr=calc_hashnr_caseup;
  55.   else
  56.     hash->calc_hashnr=calc_hashnr;
  57.   DBUG_RETURN(0);
  58. }
  59. void hash_free(HASH *hash)
  60. {
  61.   DBUG_ENTER("hash_free");
  62.   if (hash->free)
  63.   {
  64.     uint i,records;
  65.     HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
  66.     for (i=0,records=hash->records ; i < records ; i++)
  67.       (*hash->free)(data[i].data);
  68.     hash->free=0;
  69.   }
  70.   delete_dynamic(&hash->array);
  71.   hash->records=0;
  72.   DBUG_VOID_RETURN;
  73. }
  74. /* some helper functions */
  75. inline byte*
  76. hash_key(HASH *hash,const byte *record,uint *length,my_bool first)
  77. {
  78.   if (hash->get_key)
  79.     return (*hash->get_key)(record,length,first);
  80.   *length=hash->key_length;
  81.   return (byte*) record+hash->key_offset;
  82. }
  83. /* Calculate pos according to keys */
  84. static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
  85. {
  86.   if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
  87.   return (hashnr & ((buffmax >> 1) -1));
  88. }
  89. static uint hash_rec_mask(HASH *hash,HASH_LINK *pos,uint buffmax,
  90.   uint maxlength)
  91. {
  92.   uint length;
  93.   byte *key=hash_key(hash,pos->data,&length,0);
  94.   return hash_mask((*hash->calc_hashnr)(key,length),buffmax,maxlength);
  95. }
  96. #ifndef NEW_HASH_FUNCTION
  97. /* Calc hashvalue for a key */
  98. static uint calc_hashnr(const byte *key,uint length)
  99. {
  100.   register uint nr=1, nr2=4;
  101.   while (length--)
  102.   {
  103.     nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8);
  104.     nr2+=3;
  105.   }
  106.   return((uint) nr);
  107. }
  108. /* Calc hashvalue for a key, case indepenently */
  109. static uint calc_hashnr_caseup(const byte *key,uint length)
  110. {
  111.   register uint nr=1, nr2=4;
  112.   while (length--)
  113.   {
  114.     nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8);
  115.     nr2+=3;
  116.   }
  117.   return((uint) nr);
  118. }
  119. #else
  120. /*
  121.  * Fowler/Noll/Vo hash
  122.  *
  123.  * The basis of the hash algorithm was taken from an idea sent by email to the
  124.  * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and
  125.  * Glenn Fowler (gsf@research.att.com).  Landon Curt Noll (chongo@toad.com)
  126.  * later improved on their algorithm.
  127.  *
  128.  * The magic is in the interesting relationship between the special prime
  129.  * 16777619 (2^24 + 403) and 2^32 and 2^8.
  130.  *
  131.  * This hash produces the fewest collisions of any function that we've seen so
  132.  * far, and works well on both numbers and strings.
  133.  */
  134. uint calc_hashnr(const byte *key, uint len)
  135. {
  136.   const byte *end=key+len;
  137.   uint hash;
  138.   for (hash = 0; key < end; key++)
  139.   {
  140.     hash *= 16777619;
  141.     hash ^= (uint) *(uchar*) key;
  142.   }
  143.   return (hash);
  144. }
  145. uint calc_hashnr_caseup(const byte *key, uint len)
  146. {
  147.   const byte *end=key+len;
  148.   uint hash;
  149.   for (hash = 0; key < end; key++)
  150.   {
  151.     hash *= 16777619;
  152.     hash ^= (uint) (uchar) toupper(*key);
  153.   }
  154.   return (hash);
  155. }
  156. #endif
  157. inline uint rec_hashnr(HASH *hash,const byte *record)
  158. {
  159.   uint length;
  160.   byte *key=hash_key(hash,record,&length,0);
  161.   return (*hash->calc_hashnr)(key,length);
  162. }
  163. /* Search after a record based on a key */
  164. /* Sets info->current_ptr to found record */
  165. gptr hash_search(HASH *hash,const byte *key,uint length)
  166. {
  167.   HASH_LINK *pos;
  168.   uint flag,idx;
  169.   DBUG_ENTER("hash_search");
  170.   flag=1;
  171.   if (hash->records)
  172.   {
  173.     idx=hash_mask((*hash->calc_hashnr)(key,length ? length :
  174.  hash->key_length),
  175.     hash->blength,hash->records);
  176.     do
  177.     {
  178.       pos= dynamic_element(&hash->array,idx,HASH_LINK*);
  179.       if (!hashcmp(hash,pos,key,length))
  180.       {
  181. DBUG_PRINT("exit",("found key at %d",idx));
  182. hash->current_record= idx;
  183. DBUG_RETURN (pos->data);
  184.       }
  185.       if (flag)
  186.       {
  187. flag=0; /* Reset flag */
  188. if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
  189.   break; /* Wrong link */
  190.       }
  191.     }
  192.     while ((idx=pos->next) != NO_RECORD);
  193.   }
  194.   hash->current_record= NO_RECORD;
  195.   DBUG_RETURN(0);
  196. }
  197. /* Get next record with identical key */
  198. /* Can only be called if previous calls was hash_search */
  199. gptr hash_next(HASH *hash,const byte *key,uint length)
  200. {
  201.   HASH_LINK *pos;
  202.   uint idx;
  203.   if (hash->current_record != NO_RECORD)
  204.   {
  205.     HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
  206.     for (idx=data[hash->current_record].next; idx != NO_RECORD ; idx=pos->next)
  207.     {
  208.       pos=data+idx;
  209.       if (!hashcmp(hash,pos,key,length))
  210.       {
  211. hash->current_record= idx;
  212. return pos->data;
  213.       }
  214.     }
  215.     hash->current_record=NO_RECORD;
  216.   }
  217.   return 0;
  218. }
  219. /* Change link from pos to new_link */
  220. static void movelink(HASH_LINK *array,uint find,uint next_link,uint newlink)
  221. {
  222.   HASH_LINK *old_link;
  223.   do
  224.   {
  225.     old_link=array+next_link;
  226.   }
  227.   while ((next_link=old_link->next) != find);
  228.   old_link->next= newlink;
  229.   return;
  230. }
  231. /* Compare a key in a record to a whole key. Return 0 if identical */
  232. static int hashcmp(HASH *hash,HASH_LINK *pos,const byte *key,uint length)
  233. {
  234.   uint rec_keylength;
  235.   byte *rec_key=hash_key(hash,pos->data,&rec_keylength,1);
  236.   return (length && length != rec_keylength) ||
  237.     (hash->flags & HASH_CASE_INSENSITIVE ?
  238.      my_casecmp(rec_key,key,rec_keylength) :
  239.      memcmp(rec_key,key,rec_keylength));
  240. }
  241. /* Write a hash-key to the hash-index */
  242. my_bool hash_insert(HASH *info,const byte *record)
  243. {
  244.   int flag;
  245.   uint halfbuff,hash_nr,first_index,idx;
  246.   byte *ptr_to_rec,*ptr_to_rec2;
  247.   HASH_LINK *data,*empty,*gpos,*gpos2,*pos;
  248.   LINT_INIT(gpos); LINT_INIT(gpos2);
  249.   LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
  250.   flag=0;
  251.   if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array)))
  252.     return(TRUE); /* No more memory */
  253.   info->current_record= NO_RECORD;
  254.   data=dynamic_element(&info->array,0,HASH_LINK*);
  255.   halfbuff= info->blength >> 1;
  256.   idx=first_index=info->records-halfbuff;
  257.   if (idx != info->records) /* If some records */
  258.   {
  259.     do
  260.     {
  261.       pos=data+idx;
  262.       hash_nr=rec_hashnr(info,pos->data);
  263.       if (flag == 0) /* First loop; Check if ok */
  264. if (hash_mask(hash_nr,info->blength,info->records) != first_index)
  265.   break;
  266.       if (!(hash_nr & halfbuff))
  267.       { /* Key will not move */
  268. if (!(flag & LOWFIND))
  269. {
  270.   if (flag & HIGHFIND)
  271.   {
  272.     flag=LOWFIND | HIGHFIND;
  273.     /* key shall be moved to the current empty position */
  274.     gpos=empty;
  275.     ptr_to_rec=pos->data;
  276.     empty=pos; /* This place is now free */
  277.   }
  278.   else
  279.   {
  280.     flag=LOWFIND | LOWUSED; /* key isn't changed */
  281.     gpos=pos;
  282.     ptr_to_rec=pos->data;
  283.   }
  284. }
  285. else
  286. {
  287.   if (!(flag & LOWUSED))
  288.   {
  289.     /* Change link of previous LOW-key */
  290.     gpos->data=ptr_to_rec;
  291.     gpos->next=(uint) (pos-data);
  292.     flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
  293.   }
  294.   gpos=pos;
  295.   ptr_to_rec=pos->data;
  296. }
  297.       }
  298.       else
  299.       { /* key will be moved */
  300. if (!(flag & HIGHFIND))
  301. {
  302.   flag= (flag & LOWFIND) | HIGHFIND;
  303.   /* key shall be moved to the last (empty) position */
  304.   gpos2 = empty; empty=pos;
  305.   ptr_to_rec2=pos->data;
  306. }
  307. else
  308. {
  309.   if (!(flag & HIGHUSED))
  310.   {
  311.     /* Change link of previous hash-key and save */
  312.     gpos2->data=ptr_to_rec2;
  313.     gpos2->next=(uint) (pos-data);
  314.     flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
  315.   }
  316.   gpos2=pos;
  317.   ptr_to_rec2=pos->data;
  318. }
  319.       }
  320.     }
  321.     while ((idx=pos->next) != NO_RECORD);
  322.     if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
  323.     {
  324.       gpos->data=ptr_to_rec;
  325.       gpos->next=NO_RECORD;
  326.     }
  327.     if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
  328.     {
  329.       gpos2->data=ptr_to_rec2;
  330.       gpos2->next=NO_RECORD;
  331.     }
  332.   }
  333.   /* Check if we are at the empty position */
  334.   idx=hash_mask(rec_hashnr(info,record),info->blength,info->records+1);
  335.   pos=data+idx;
  336.   if (pos == empty)
  337.   {
  338.     pos->data=(byte*) record;
  339.     pos->next=NO_RECORD;
  340.   }
  341.   else
  342.   {
  343.     /* Check if more records in same hash-nr family */
  344.     empty[0]=pos[0];
  345.     gpos=data+hash_rec_mask(info,pos,info->blength,info->records+1);
  346.     if (pos == gpos)
  347.     {
  348.       pos->data=(byte*) record;
  349.       pos->next=(uint) (empty - data);
  350.     }
  351.     else
  352.     {
  353.       pos->data=(byte*) record;
  354.       pos->next=NO_RECORD;
  355.       movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));
  356.     }
  357.   }
  358.   if (++info->records == info->blength)
  359.     info->blength+= info->blength;
  360.   return(0);
  361. }
  362. /******************************************************************************
  363. ** Remove one record from hash-table. The record with the same record
  364. ** ptr is removed.
  365. ** if there is a free-function it's called for record if found
  366. ******************************************************************************/
  367. my_bool hash_delete(HASH *hash,byte *record)
  368. {
  369.   uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
  370.   HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
  371.   DBUG_ENTER("hash_delete");
  372.   if (!hash->records)
  373.     DBUG_RETURN(1);
  374.   blength=hash->blength;
  375.   data=dynamic_element(&hash->array,0,HASH_LINK*);
  376.   /* Search after record with key */
  377.   pos=data+ hash_mask(rec_hashnr(hash,record),blength,hash->records);
  378.   gpos = 0;
  379.   while (pos->data != record)
  380.   {
  381.     gpos=pos;
  382.     if (pos->next == NO_RECORD)
  383.       DBUG_RETURN(1); /* Key not found */
  384.     pos=data+pos->next;
  385.   }
  386.   if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
  387.   hash->current_record= NO_RECORD;
  388.   lastpos=data+hash->records;
  389.   /* Remove link to record */
  390.   empty=pos; empty_index=(uint) (empty-data);
  391.   if (gpos)
  392.     gpos->next=pos->next; /* unlink current ptr */
  393.   else if (pos->next != NO_RECORD)
  394.   {
  395.     empty=data+(empty_index=pos->next);
  396.     pos->data=empty->data;
  397.     pos->next=empty->next;
  398.   }
  399.   if (empty == lastpos) /* last key at wrong pos or no next link */
  400.     goto exit;
  401.   /* Move the last key (lastpos) */
  402.   lastpos_hashnr=rec_hashnr(hash,lastpos->data);
  403.   /* pos is where lastpos should be */
  404.   pos=data+hash_mask(lastpos_hashnr,hash->blength,hash->records);
  405.   if (pos == empty) /* Move to empty position. */
  406.   {
  407.     empty[0]=lastpos[0];
  408.     goto exit;
  409.   }
  410.   pos_hashnr=rec_hashnr(hash,pos->data);
  411.   /* pos3 is where the pos should be */
  412.   pos3= data+hash_mask(pos_hashnr,hash->blength,hash->records);
  413.   if (pos != pos3)
  414.   { /* pos is on wrong posit */
  415.     empty[0]=pos[0]; /* Save it here */
  416.     pos[0]=lastpos[0]; /* This should be here */
  417.     movelink(data,(uint) (pos-data),(uint) (pos3-data),empty_index);
  418.     goto exit;
  419.   }
  420.   pos2= hash_mask(lastpos_hashnr,blength,hash->records+1);
  421.   if (pos2 == hash_mask(pos_hashnr,blength,hash->records+1))
  422.   { /* Identical key-positions */
  423.     if (pos2 != hash->records)
  424.     {
  425.       empty[0]=lastpos[0];
  426.       movelink(data,(uint) (lastpos-data),(uint) (pos-data),empty_index);
  427.       goto exit;
  428.     }
  429.     idx= (uint) (pos-data); /* Link pos->next after lastpos */
  430.   }
  431.   else idx= NO_RECORD; /* Different positions merge */
  432.   empty[0]=lastpos[0];
  433.   movelink(data,idx,empty_index,pos->next);
  434.   pos->next=empty_index;
  435. exit:
  436.   VOID(pop_dynamic(&hash->array));
  437.   if (hash->free)
  438.     (*hash->free)((byte*) record);
  439.   DBUG_RETURN(0);
  440. }
  441. /*
  442.   Update keys when record has changed.
  443.   This is much more efficent than using a delete & insert.
  444.   */
  445. my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length)
  446. {
  447.   uint idx,new_index,new_pos_index,blength,records,empty;
  448.   HASH_LINK org_link,*data,*previous,*pos;
  449.   DBUG_ENTER("hash_update");
  450.   data=dynamic_element(&hash->array,0,HASH_LINK*);
  451.   blength=hash->blength; records=hash->records;
  452.   /* Search after record with key */
  453.   idx=hash_mask((*hash->calc_hashnr)(old_key,(old_key_length ?
  454. old_key_length :
  455. hash->key_length)),
  456.   blength,records);
  457.   new_index=hash_mask(rec_hashnr(hash,record),blength,records);
  458.   if (idx == new_index)
  459.     DBUG_RETURN(0); /* Nothing to do (No record check) */
  460.   previous=0;
  461.   for (;;)
  462.   {
  463.     if ((pos= data+idx)->data == record)
  464.       break;
  465.     previous=pos;
  466.     if ((idx=pos->next) == NO_RECORD)
  467.       DBUG_RETURN(1); /* Not found in links */
  468.   }
  469.   hash->current_record= NO_RECORD;
  470.   org_link= *pos;
  471.   empty=idx;
  472.   /* Relink record from current chain */
  473.   if (!previous)
  474.   {
  475.     if (pos->next != NO_RECORD)
  476.     {
  477.       empty=pos->next;
  478.       *pos= data[pos->next];
  479.     }
  480.   }
  481.   else
  482.     previous->next=pos->next; /* unlink pos */
  483.   /* Move data to correct position */
  484.   pos=data+new_index;
  485.   new_pos_index=hash_rec_mask(hash,pos,blength,records);
  486.   if (new_index != new_pos_index)
  487.   { /* Other record in wrong position */
  488.     data[empty] = *pos;
  489.     movelink(data,new_index,new_pos_index,empty);
  490.     org_link.next=NO_RECORD;
  491.     data[new_index]= org_link;
  492.   }
  493.   else
  494.   { /* Link in chain at right position */
  495.     org_link.next=data[new_index].next;
  496.     data[empty]=org_link;
  497.     data[new_index].next=empty;
  498.   }
  499.   DBUG_RETURN(0);
  500. }
  501. byte *hash_element(HASH *hash,uint idx)
  502. {
  503.   if (idx < hash->records)
  504.     return dynamic_element(&hash->array,idx,HASH_LINK*)->data;
  505.   return 0;
  506. }
  507. #ifndef DBUG_OFF
  508. my_bool hash_check(HASH *hash)
  509. {
  510.   int error;
  511.   uint i,rec_link,found,max_links,seek,links,idx;
  512.   uint records,blength;
  513.   HASH_LINK *data,*hash_info;
  514.   records=hash->records; blength=hash->blength;
  515.   data=dynamic_element(&hash->array,0,HASH_LINK*);
  516.   error=0;
  517.   for (i=found=max_links=seek=0 ; i < records ; i++)
  518.   {
  519.     if (hash_rec_mask(hash,data+i,blength,records) == i)
  520.     {
  521.       found++; seek++; links=1;
  522.       for (idx=data[i].next ;
  523.    idx != NO_RECORD && found < records + 1;
  524.    idx=hash_info->next)
  525.       {
  526. if (idx >= records)
  527. {
  528.   DBUG_PRINT("error",
  529.      ("Found pointer outside array to %d from link starting at %d",
  530.       idx,i));
  531.   error=1;
  532. }
  533. hash_info=data+idx;
  534. seek+= ++links;
  535. if ((rec_link=hash_rec_mask(hash,hash_info,blength,records)) != i)
  536. {
  537.   DBUG_PRINT("error",
  538.      ("Record in wrong link at %d: Start %d  Record: %lx  Record-link %d", idx,i,hash_info->data,rec_link));
  539.   error=1;
  540. }
  541. else
  542.   found++;
  543.       }
  544.       if (links > max_links) max_links=links;
  545.     }
  546.   }
  547.   if (found != records)
  548.   {
  549.     DBUG_PRINT("error",("Found %ld of %ld records"));
  550.     error=1;
  551.   }
  552.   if (records)
  553.     DBUG_PRINT("info",
  554.        ("records: %ld   seeks: %d   max links: %d   hitrate: %.2f",
  555. records,seek,max_links,(float) seek / (float) records));
  556.   return error;
  557. }
  558. #endif