Drizzled Public API Documentation

my_hash.cc
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 /* The hash functions used for saving keys */
00021 /* One of key_length or key_length_offset must be given */
00022 /* Key length of 0 isn't allowed */
00023 
00024 #include <config.h>
00025 #include <drizzled/my_hash.h>
00026 #include <drizzled/charset.h>
00027 #include <vector>
00028 
00029 namespace drizzled {
00030 
00031 const uint32_t NO_RECORD= UINT32_MAX;
00032 
00033 const int LOWFIND= 1;
00034 const int LOWUSED= 2;
00035 const int HIGHFIND= 4;
00036 const int HIGHUSED= 8;
00037 
00038 static uint32_t hash_mask(uint32_t hashnr, uint32_t buffmax, uint32_t maxlength);
00039 static void movelink(HASH_LINK *array, uint32_t pos, uint32_t next_link, uint32_t newlink);
00040 static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key, size_t length);
00041 
00042 static uint32_t calc_hash(const HASH *hash, const unsigned char *key, size_t length)
00043 {
00044   uint32_t nr1=1, nr2=4;
00045   hash->charset->coll->hash_sort(hash->charset, key,length, &nr1, &nr2);
00046   return nr1;
00047 }
00048 
00049 #define dynamic_element(array,array_index,type) ((type)((array)->buffer) +(array_index))
00050 
00051 void
00052 _hash_init(HASH *hash,uint32_t growth_size, const charset_info_st * const charset,
00053            uint32_t size, size_t key_offset, size_t key_length,
00054            hash_get_key get_key,
00055            hash_free_key free_element, uint32_t flags)
00056 {
00057   hash->records=0;
00058   hash->array.init(sizeof(HASH_LINK), size, growth_size);
00059   hash->key_offset=key_offset;
00060   hash->key_length=key_length;
00061   hash->blength=1;
00062   hash->get_key=get_key;
00063   hash->free=free_element;
00064   hash->flags=flags;
00065   hash->charset=charset;
00066 }
00067 
00068 
00069 /*
00070   Call hash->free on all elements in hash.
00071 
00072   SYNOPSIS
00073   hash_free_elements()
00074   hash   hash table
00075 
00076   NOTES:
00077   Sets records to 0
00078 */
00079 
00080 static inline void hash_free_elements(HASH *hash)
00081 {
00082   if (hash->free)
00083   {
00084     HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*);
00085     HASH_LINK *end= data + hash->records;
00086     while (data < end)
00087       (*hash->free)((data++)->data);
00088   }
00089   hash->records=0;
00090 }
00091 
00092 
00093 /*
00094   Free memory used by hash.
00095 
00096   SYNOPSIS
00097   hash_free()
00098   hash   the hash to delete elements of
00099 
00100   NOTES: Hash can't be reused without calling hash_init again.
00101 */
00102 
00103 void hash_free(HASH *hash)
00104 {
00105   hash_free_elements(hash);
00106   hash->free= 0;
00107   hash->array.free();
00108 }
00109 
00110 /* some helper functions */
00111 
00112 /*
00113   This function is char* instead of unsigned char* as HPUX11 compiler can't
00114   handle inline functions that are not defined as native types
00115 */
00116 
00117 static inline char*
00118 hash_key(const HASH *hash, const unsigned char *record, size_t *length,
00119          bool first)
00120 {
00121   if (hash->get_key)
00122     return (char*) (*hash->get_key)(record,length,first);
00123   *length=hash->key_length;
00124   return (char*) record+hash->key_offset;
00125 }
00126 
00127 /* Calculate pos according to keys */
00128 
00129 static uint32_t hash_mask(uint32_t hashnr,uint32_t buffmax,uint32_t maxlength)
00130 {
00131   if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
00132   return (hashnr & ((buffmax >> 1) -1));
00133 }
00134 
00135 static uint32_t hash_rec_mask(const HASH *hash, HASH_LINK *pos,
00136                               uint32_t buffmax, uint32_t maxlength)
00137 {
00138   size_t length;
00139   unsigned char *key= (unsigned char*) hash_key(hash,pos->data,&length,0);
00140   return hash_mask(calc_hash(hash,key,length),buffmax,maxlength);
00141 }
00142 
00143 
00144 
00145 static
00146 inline
00147 unsigned int rec_hashnr(HASH *hash,const unsigned char *record)
00148 {
00149   size_t length;
00150   unsigned char *key= (unsigned char*) hash_key(hash,record,&length,0);
00151   return calc_hash(hash,key,length);
00152 }
00153 
00154 
00155 unsigned char* hash_search(const HASH *hash, const unsigned char *key,
00156                            size_t length)
00157 {
00158   HASH_SEARCH_STATE state;
00159   return hash_first(hash, key, length, &state);
00160 }
00161 
00162 /*
00163   Search after a record based on a key
00164 
00165   NOTE
00166   Assigns the number of the found record to HASH_SEARCH_STATE state
00167 */
00168 
00169 unsigned char* hash_first(const HASH *hash, const unsigned char *key,
00170                           size_t length,
00171                           HASH_SEARCH_STATE *current_record)
00172 {
00173   HASH_LINK *pos;
00174   uint32_t flag,idx;
00175 
00176   flag=1;
00177   if (hash->records)
00178   {
00179     idx=hash_mask(calc_hash(hash,key,length ? length : hash->key_length),
00180                   hash->blength,hash->records);
00181     do
00182     {
00183       pos= dynamic_element(&hash->array,idx,HASH_LINK*);
00184       if (!hashcmp(hash,pos,key,length))
00185       {
00186         *current_record= idx;
00187         return (pos->data);
00188       }
00189       if (flag)
00190       {
00191         /* Reset flag */
00192         flag=0;
00193         if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
00194           /* Wrong link */
00195           break;
00196       }
00197     }
00198     while ((idx=pos->next) != NO_RECORD);
00199   }
00200   *current_record= NO_RECORD;
00201   return 0;
00202 }
00203 
00204 /* Change link from pos to new_link */
00205 
00206 static void movelink(HASH_LINK *array, uint32_t find,
00207                      uint32_t next_link, uint32_t newlink)
00208 {
00209   HASH_LINK *old_link;
00210   do
00211   {
00212     old_link=array+next_link;
00213   }
00214   while ((next_link=old_link->next) != find);
00215   old_link->next= newlink;
00216   return;
00217 }
00218 
00219 /*
00220   Compare a key in a record to a whole key. Return 0 if identical
00221 
00222   SYNOPSIS
00223   hashcmp()
00224   hash   hash table
00225   pos    position of hash record to use in comparison
00226   key    key for comparison
00227   length length of key
00228 
00229   NOTES:
00230   If length is 0, comparison is done using the length of the
00231   record being compared against.
00232 
00233   RETURN
00234   = 0  key of record == key
00235   != 0 key of record != key
00236 */
00237 
00238 static int hashcmp(const HASH *hash, HASH_LINK *pos, const unsigned char *key,
00239                    size_t length)
00240 {
00241   size_t rec_keylength;
00242   unsigned char *rec_key= (unsigned char*) hash_key(hash, pos->data,
00243                                                     &rec_keylength,1);
00244   return ((length && length != rec_keylength) ||
00245           my_strnncoll(hash->charset, rec_key, rec_keylength,
00246                        key, rec_keylength));
00247 }
00248 
00249 
00250 /* Write a hash-key to the hash-index */
00251 
00252 bool my_hash_insert(HASH *info,const unsigned char *record)
00253 {
00254   int flag;
00255   size_t idx;
00256   uint32_t halfbuff,hash_nr,first_index;
00257   unsigned char *ptr_to_rec=NULL,*ptr_to_rec2=NULL;
00258   HASH_LINK *data,*empty,*gpos=NULL,*gpos2=NULL,*pos;
00259 
00260   if (HASH_UNIQUE & info->flags)
00261   {
00262     unsigned char *key= (unsigned char*) hash_key(info, record, &idx, 1);
00263     if (hash_search(info, key, idx))
00264       /* Duplicate entry */
00265       return true;
00266   }
00267 
00268   flag=0;
00269   empty=(HASH_LINK*)info->array.alloc();
00270 
00271   data=dynamic_element(&info->array,0,HASH_LINK*);
00272   halfbuff= info->blength >> 1;
00273 
00274   idx= first_index= info->records-halfbuff;
00275   /* If some records */
00276   if (idx != info->records)
00277   {
00278     do
00279     {
00280       pos=data+idx;
00281       hash_nr=rec_hashnr(info,pos->data);
00282       /* First loop; Check if ok */
00283       if (flag == 0)
00284         if (hash_mask(hash_nr,info->blength,info->records) != first_index)
00285           break;
00286       if (!(hash_nr & halfbuff))
00287       {
00288         /* Key will not move */
00289         if (!(flag & LOWFIND))
00290         {
00291           if (flag & HIGHFIND)
00292           {
00293             flag=LOWFIND | HIGHFIND;
00294             /* key shall be moved to the current empty position */
00295             gpos=empty;
00296             ptr_to_rec=pos->data;
00297             /* This place is now free */
00298             empty=pos;
00299           }
00300           else
00301           {
00302             /* key isn't changed */
00303             flag=LOWFIND | LOWUSED;
00304             gpos=pos;
00305             ptr_to_rec=pos->data;
00306           }
00307         }
00308         else
00309         {
00310           if (!(flag & LOWUSED))
00311           {
00312             /* Change link of previous LOW-key */
00313             gpos->data=ptr_to_rec;
00314             gpos->next= (uint32_t) (pos-data);
00315             flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
00316           }
00317           gpos=pos;
00318           ptr_to_rec=pos->data;
00319         }
00320       }
00321       else
00322       {
00323         /* key will be moved */
00324         if (!(flag & HIGHFIND))
00325         {
00326           flag= (flag & LOWFIND) | HIGHFIND;
00327           /* key shall be moved to the last (empty) position */
00328           gpos2 = empty; empty=pos;
00329           ptr_to_rec2=pos->data;
00330         }
00331         else
00332         {
00333           if (!(flag & HIGHUSED))
00334           {
00335             /* Change link of previous hash-key and save */
00336             gpos2->data=ptr_to_rec2;
00337             gpos2->next=(uint32_t) (pos-data);
00338             flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
00339           }
00340           gpos2=pos;
00341           ptr_to_rec2=pos->data;
00342         }
00343       }
00344     }
00345     while ((idx=pos->next) != NO_RECORD);
00346 
00347     if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
00348     {
00349       gpos->data=ptr_to_rec;
00350       gpos->next=NO_RECORD;
00351     }
00352     if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
00353     {
00354       gpos2->data=ptr_to_rec2;
00355       gpos2->next=NO_RECORD;
00356     }
00357   }
00358   /* Check if we are at the empty position */
00359 
00360   idx=hash_mask(rec_hashnr(info,record),info->blength,info->records+1);
00361   pos=data+idx;
00362   if (pos == empty)
00363   {
00364     pos->data=(unsigned char*) record;
00365     pos->next=NO_RECORD;
00366   }
00367   else
00368   {
00369     /* Check if more records in same hash-nr family */
00370     empty[0]=pos[0];
00371     gpos=data+hash_rec_mask(info,pos,info->blength,info->records+1);
00372     if (pos == gpos)
00373     {
00374       pos->data=(unsigned char*) record;
00375       pos->next=(uint32_t) (empty - data);
00376     }
00377     else
00378     {
00379       pos->data=(unsigned char*) record;
00380       pos->next=NO_RECORD;
00381       movelink(data,(uint32_t) (pos-data),(uint32_t) (gpos-data),(uint32_t) (empty-data));
00382     }
00383   }
00384   if (++info->records == info->blength)
00385     info->blength+= info->blength;
00386   return 0;
00387 }
00388 
00389 } /* namespace drizzled */