Back to index

lightning-sunbird  0.9+nobinonly
hash.c
Go to the documentation of this file.
00001 /*
00002 ** 2001 September 22
00003 **
00004 ** The author disclaims copyright to this source code.  In place of
00005 ** a legal notice, here is a blessing:
00006 **
00007 **    May you do good and not evil.
00008 **    May you find forgiveness for yourself and forgive others.
00009 **    May you share freely, never taking more than you give.
00010 **
00011 *************************************************************************
00012 ** This is the implementation of generic hash-tables
00013 ** used in SQLite.
00014 **
00015 ** $Id: hash.c,v 1.18 2006/02/14 10:48:39 danielk1977 Exp $
00016 */
00017 #include "sqliteInt.h"
00018 #include <assert.h>
00019 
00020 /* Turn bulk memory into a hash table object by initializing the
00021 ** fields of the Hash structure.
00022 **
00023 ** "pNew" is a pointer to the hash table that is to be initialized.
00024 ** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER,
00025 ** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING.  The value of keyClass 
00026 ** determines what kind of key the hash table will use.  "copyKey" is
00027 ** true if the hash table should make its own private copy of keys and
00028 ** false if it should just use the supplied pointer.  CopyKey only makes
00029 ** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored
00030 ** for other key classes.
00031 */
00032 void sqlite3HashInit(Hash *pNew, int keyClass, int copyKey){
00033   assert( pNew!=0 );
00034   assert( keyClass>=SQLITE_HASH_STRING && keyClass<=SQLITE_HASH_BINARY );
00035   pNew->keyClass = keyClass;
00036 #if 0
00037   if( keyClass==SQLITE_HASH_POINTER || keyClass==SQLITE_HASH_INT ) copyKey = 0;
00038 #endif
00039   pNew->copyKey = copyKey;
00040   pNew->first = 0;
00041   pNew->count = 0;
00042   pNew->htsize = 0;
00043   pNew->ht = 0;
00044   pNew->xMalloc = sqlite3MallocX;
00045   pNew->xFree = sqlite3FreeX;
00046 }
00047 
00048 /* Remove all entries from a hash table.  Reclaim all memory.
00049 ** Call this routine to delete a hash table or to reset a hash table
00050 ** to the empty state.
00051 */
00052 void sqlite3HashClear(Hash *pH){
00053   HashElem *elem;         /* For looping over all elements of the table */
00054 
00055   assert( pH!=0 );
00056   elem = pH->first;
00057   pH->first = 0;
00058   if( pH->ht ) pH->xFree(pH->ht);
00059   pH->ht = 0;
00060   pH->htsize = 0;
00061   while( elem ){
00062     HashElem *next_elem = elem->next;
00063     if( pH->copyKey && elem->pKey ){
00064       pH->xFree(elem->pKey);
00065     }
00066     pH->xFree(elem);
00067     elem = next_elem;
00068   }
00069   pH->count = 0;
00070 }
00071 
00072 #if 0 /* NOT USED */
00073 /*
00074 ** Hash and comparison functions when the mode is SQLITE_HASH_INT
00075 */
00076 static int intHash(const void *pKey, int nKey){
00077   return nKey ^ (nKey<<8) ^ (nKey>>8);
00078 }
00079 static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
00080   return n2 - n1;
00081 }
00082 #endif
00083 
00084 #if 0 /* NOT USED */
00085 /*
00086 ** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
00087 */
00088 static int ptrHash(const void *pKey, int nKey){
00089   uptr x = Addr(pKey);
00090   return x ^ (x<<8) ^ (x>>8);
00091 }
00092 static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
00093   if( pKey1==pKey2 ) return 0;
00094   if( pKey1<pKey2 ) return -1;
00095   return 1;
00096 }
00097 #endif
00098 
00099 /*
00100 ** Hash and comparison functions when the mode is SQLITE_HASH_STRING
00101 */
00102 static int strHash(const void *pKey, int nKey){
00103   const char *z = (const char *)pKey;
00104   int h = 0;
00105   if( nKey<=0 ) nKey = strlen(z);
00106   while( nKey > 0  ){
00107     h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
00108     nKey--;
00109   }
00110   return h & 0x7fffffff;
00111 }
00112 static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
00113   if( n1!=n2 ) return 1;
00114   return sqlite3StrNICmp((const char*)pKey1,(const char*)pKey2,n1);
00115 }
00116 
00117 /*
00118 ** Hash and comparison functions when the mode is SQLITE_HASH_BINARY
00119 */
00120 static int binHash(const void *pKey, int nKey){
00121   int h = 0;
00122   const char *z = (const char *)pKey;
00123   while( nKey-- > 0 ){
00124     h = (h<<3) ^ h ^ *(z++);
00125   }
00126   return h & 0x7fffffff;
00127 }
00128 static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
00129   if( n1!=n2 ) return 1;
00130   return memcmp(pKey1,pKey2,n1);
00131 }
00132 
00133 /*
00134 ** Return a pointer to the appropriate hash function given the key class.
00135 **
00136 ** The C syntax in this function definition may be unfamilar to some 
00137 ** programmers, so we provide the following additional explanation:
00138 **
00139 ** The name of the function is "hashFunction".  The function takes a
00140 ** single parameter "keyClass".  The return value of hashFunction()
00141 ** is a pointer to another function.  Specifically, the return value
00142 ** of hashFunction() is a pointer to a function that takes two parameters
00143 ** with types "const void*" and "int" and returns an "int".
00144 */
00145 static int (*hashFunction(int keyClass))(const void*,int){
00146 #if 0  /* HASH_INT and HASH_POINTER are never used */
00147   switch( keyClass ){
00148     case SQLITE_HASH_INT:     return &intHash;
00149     case SQLITE_HASH_POINTER: return &ptrHash;
00150     case SQLITE_HASH_STRING:  return &strHash;
00151     case SQLITE_HASH_BINARY:  return &binHash;;
00152     default: break;
00153   }
00154   return 0;
00155 #else
00156   if( keyClass==SQLITE_HASH_STRING ){
00157     return &strHash;
00158   }else{
00159     assert( keyClass==SQLITE_HASH_BINARY );
00160     return &binHash;
00161   }
00162 #endif
00163 }
00164 
00165 /*
00166 ** Return a pointer to the appropriate hash function given the key class.
00167 **
00168 ** For help in interpreted the obscure C code in the function definition,
00169 ** see the header comment on the previous function.
00170 */
00171 static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
00172 #if 0 /* HASH_INT and HASH_POINTER are never used */
00173   switch( keyClass ){
00174     case SQLITE_HASH_INT:     return &intCompare;
00175     case SQLITE_HASH_POINTER: return &ptrCompare;
00176     case SQLITE_HASH_STRING:  return &strCompare;
00177     case SQLITE_HASH_BINARY:  return &binCompare;
00178     default: break;
00179   }
00180   return 0;
00181 #else
00182   if( keyClass==SQLITE_HASH_STRING ){
00183     return &strCompare;
00184   }else{
00185     assert( keyClass==SQLITE_HASH_BINARY );
00186     return &binCompare;
00187   }
00188 #endif
00189 }
00190 
00191 /* Link an element into the hash table
00192 */
00193 static void insertElement(
00194   Hash *pH,              /* The complete hash table */
00195   struct _ht *pEntry,    /* The entry into which pNew is inserted */
00196   HashElem *pNew         /* The element to be inserted */
00197 ){
00198   HashElem *pHead;       /* First element already in pEntry */
00199   pHead = pEntry->chain;
00200   if( pHead ){
00201     pNew->next = pHead;
00202     pNew->prev = pHead->prev;
00203     if( pHead->prev ){ pHead->prev->next = pNew; }
00204     else             { pH->first = pNew; }
00205     pHead->prev = pNew;
00206   }else{
00207     pNew->next = pH->first;
00208     if( pH->first ){ pH->first->prev = pNew; }
00209     pNew->prev = 0;
00210     pH->first = pNew;
00211   }
00212   pEntry->count++;
00213   pEntry->chain = pNew;
00214 }
00215 
00216 
00217 /* Resize the hash table so that it cantains "new_size" buckets.
00218 ** "new_size" must be a power of 2.  The hash table might fail 
00219 ** to resize if sqliteMalloc() fails.
00220 */
00221 static void rehash(Hash *pH, int new_size){
00222   struct _ht *new_ht;            /* The new hash table */
00223   HashElem *elem, *next_elem;    /* For looping over existing elements */
00224   int (*xHash)(const void*,int); /* The hash function */
00225 
00226   assert( (new_size & (new_size-1))==0 );
00227   new_ht = (struct _ht *)pH->xMalloc( new_size*sizeof(struct _ht) );
00228   if( new_ht==0 ) return;
00229   if( pH->ht ) pH->xFree(pH->ht);
00230   pH->ht = new_ht;
00231   pH->htsize = new_size;
00232   xHash = hashFunction(pH->keyClass);
00233   for(elem=pH->first, pH->first=0; elem; elem = next_elem){
00234     int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
00235     next_elem = elem->next;
00236     insertElement(pH, &new_ht[h], elem);
00237   }
00238 }
00239 
00240 /* This function (for internal use only) locates an element in an
00241 ** hash table that matches the given key.  The hash for this key has
00242 ** already been computed and is passed as the 4th parameter.
00243 */
00244 static HashElem *findElementGivenHash(
00245   const Hash *pH,     /* The pH to be searched */
00246   const void *pKey,   /* The key we are searching for */
00247   int nKey,
00248   int h               /* The hash for this key. */
00249 ){
00250   HashElem *elem;                /* Used to loop thru the element list */
00251   int count;                     /* Number of elements left to test */
00252   int (*xCompare)(const void*,int,const void*,int);  /* comparison function */
00253 
00254   if( pH->ht ){
00255     struct _ht *pEntry = &pH->ht[h];
00256     elem = pEntry->chain;
00257     count = pEntry->count;
00258     xCompare = compareFunction(pH->keyClass);
00259     while( count-- && elem ){
00260       if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ 
00261         return elem;
00262       }
00263       elem = elem->next;
00264     }
00265   }
00266   return 0;
00267 }
00268 
00269 /* Remove a single entry from the hash table given a pointer to that
00270 ** element and a hash on the element's key.
00271 */
00272 static void removeElementGivenHash(
00273   Hash *pH,         /* The pH containing "elem" */
00274   HashElem* elem,   /* The element to be removed from the pH */
00275   int h             /* Hash value for the element */
00276 ){
00277   struct _ht *pEntry;
00278   if( elem->prev ){
00279     elem->prev->next = elem->next; 
00280   }else{
00281     pH->first = elem->next;
00282   }
00283   if( elem->next ){
00284     elem->next->prev = elem->prev;
00285   }
00286   pEntry = &pH->ht[h];
00287   if( pEntry->chain==elem ){
00288     pEntry->chain = elem->next;
00289   }
00290   pEntry->count--;
00291   if( pEntry->count<=0 ){
00292     pEntry->chain = 0;
00293   }
00294   if( pH->copyKey && elem->pKey ){
00295     pH->xFree(elem->pKey);
00296   }
00297   pH->xFree( elem );
00298   pH->count--;
00299   if( pH->count<=0 ){
00300     assert( pH->first==0 );
00301     assert( pH->count==0 );
00302     sqlite3HashClear(pH);
00303   }
00304 }
00305 
00306 /* Attempt to locate an element of the hash table pH with a key
00307 ** that matches pKey,nKey.  Return the data for this element if it is
00308 ** found, or NULL if there is no match.
00309 */
00310 void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
00311   int h;             /* A hash on key */
00312   HashElem *elem;    /* The element that matches key */
00313   int (*xHash)(const void*,int);  /* The hash function */
00314 
00315   if( pH==0 || pH->ht==0 ) return 0;
00316   xHash = hashFunction(pH->keyClass);
00317   assert( xHash!=0 );
00318   h = (*xHash)(pKey,nKey);
00319   assert( (pH->htsize & (pH->htsize-1))==0 );
00320   elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
00321   return elem ? elem->data : 0;
00322 }
00323 
00324 /* Insert an element into the hash table pH.  The key is pKey,nKey
00325 ** and the data is "data".
00326 **
00327 ** If no element exists with a matching key, then a new
00328 ** element is created.  A copy of the key is made if the copyKey
00329 ** flag is set.  NULL is returned.
00330 **
00331 ** If another element already exists with the same key, then the
00332 ** new data replaces the old data and the old data is returned.
00333 ** The key is not copied in this instance.  If a malloc fails, then
00334 ** the new data is returned and the hash table is unchanged.
00335 **
00336 ** If the "data" parameter to this function is NULL, then the
00337 ** element corresponding to "key" is removed from the hash table.
00338 */
00339 void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
00340   int hraw;             /* Raw hash value of the key */
00341   int h;                /* the hash of the key modulo hash table size */
00342   HashElem *elem;       /* Used to loop thru the element list */
00343   HashElem *new_elem;   /* New element added to the pH */
00344   int (*xHash)(const void*,int);  /* The hash function */
00345 
00346   assert( pH!=0 );
00347   xHash = hashFunction(pH->keyClass);
00348   assert( xHash!=0 );
00349   hraw = (*xHash)(pKey, nKey);
00350   assert( (pH->htsize & (pH->htsize-1))==0 );
00351   h = hraw & (pH->htsize-1);
00352   elem = findElementGivenHash(pH,pKey,nKey,h);
00353   if( elem ){
00354     void *old_data = elem->data;
00355     if( data==0 ){
00356       removeElementGivenHash(pH,elem,h);
00357     }else{
00358       elem->data = data;
00359     }
00360     return old_data;
00361   }
00362   if( data==0 ) return 0;
00363   new_elem = (HashElem*)pH->xMalloc( sizeof(HashElem) );
00364   if( new_elem==0 ) return data;
00365   if( pH->copyKey && pKey!=0 ){
00366     new_elem->pKey = pH->xMalloc( nKey );
00367     if( new_elem->pKey==0 ){
00368       pH->xFree(new_elem);
00369       return data;
00370     }
00371     memcpy((void*)new_elem->pKey, pKey, nKey);
00372   }else{
00373     new_elem->pKey = (void*)pKey;
00374   }
00375   new_elem->nKey = nKey;
00376   pH->count++;
00377   if( pH->htsize==0 ){
00378     rehash(pH,8);
00379     if( pH->htsize==0 ){
00380       pH->count = 0;
00381       pH->xFree(new_elem);
00382       return data;
00383     }
00384   }
00385   if( pH->count > pH->htsize ){
00386     rehash(pH,pH->htsize*2);
00387   }
00388   assert( pH->htsize>0 );
00389   assert( (pH->htsize & (pH->htsize-1))==0 );
00390   h = hraw & (pH->htsize-1);
00391   insertElement(pH, &pH->ht[h], new_elem);
00392   new_elem->data = data;
00393   return 0;
00394 }