fts1_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 used in SQLite.
00013 ** We've modified it slightly to serve as a standalone hash table
00014 ** implementation for the full-text indexing module.
00015 */
00016 #include <assert.h>
00017 #include <stdlib.h>
00018 #include <string.h>
00019 
00020 /*
00021 ** The code in this file is only compiled if:
00022 **
00023 **     * The FTS1 module is being built as an extension
00024 **       (in which case SQLITE_CORE is not defined), or
00025 **
00026 **     * The FTS1 module is being built into the core of
00027 **       SQLite (in which case SQLITE_ENABLE_FTS1 is defined).
00028 */
00029 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1)
00030 
00031 
00032 #include "fts1_hash.h"
00033 
00034 static void *malloc_and_zero(int n){
00035   void *p = malloc(n);
00036   if( p ){
00037     memset(p, 0, n);
00038   }
00039   return p;
00040 }
00041 
00042 /* Turn bulk memory into a hash table object by initializing the
00043 ** fields of the Hash structure.
00044 **
00045 ** "pNew" is a pointer to the hash table that is to be initialized.
00046 ** keyClass is one of the constants 
00047 ** FTS1_HASH_BINARY or FTS1_HASH_STRING.  The value of keyClass 
00048 ** determines what kind of key the hash table will use.  "copyKey" is
00049 ** true if the hash table should make its own private copy of keys and
00050 ** false if it should just use the supplied pointer.
00051 */
00052 void sqlite3Fts1HashInit(fts1Hash *pNew, int keyClass, int copyKey){
00053   assert( pNew!=0 );
00054   assert( keyClass>=FTS1_HASH_STRING && keyClass<=FTS1_HASH_BINARY );
00055   pNew->keyClass = keyClass;
00056   pNew->copyKey = copyKey;
00057   pNew->first = 0;
00058   pNew->count = 0;
00059   pNew->htsize = 0;
00060   pNew->ht = 0;
00061   pNew->xMalloc = malloc_and_zero;
00062   pNew->xFree = free;
00063 }
00064 
00065 /* Remove all entries from a hash table.  Reclaim all memory.
00066 ** Call this routine to delete a hash table or to reset a hash table
00067 ** to the empty state.
00068 */
00069 void sqlite3Fts1HashClear(fts1Hash *pH){
00070   fts1HashElem *elem;         /* For looping over all elements of the table */
00071 
00072   assert( pH!=0 );
00073   elem = pH->first;
00074   pH->first = 0;
00075   if( pH->ht ) pH->xFree(pH->ht);
00076   pH->ht = 0;
00077   pH->htsize = 0;
00078   while( elem ){
00079     fts1HashElem *next_elem = elem->next;
00080     if( pH->copyKey && elem->pKey ){
00081       pH->xFree(elem->pKey);
00082     }
00083     pH->xFree(elem);
00084     elem = next_elem;
00085   }
00086   pH->count = 0;
00087 }
00088 
00089 /*
00090 ** Hash and comparison functions when the mode is FTS1_HASH_STRING
00091 */
00092 static int strHash(const void *pKey, int nKey){
00093   const char *z = (const char *)pKey;
00094   int h = 0;
00095   if( nKey<=0 ) nKey = (int) strlen(z);
00096   while( nKey > 0  ){
00097     h = (h<<3) ^ h ^ *z++;
00098     nKey--;
00099   }
00100   return h & 0x7fffffff;
00101 }
00102 static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
00103   if( n1!=n2 ) return 1;
00104   return strncmp((const char*)pKey1,(const char*)pKey2,n1);
00105 }
00106 
00107 /*
00108 ** Hash and comparison functions when the mode is FTS1_HASH_BINARY
00109 */
00110 static int binHash(const void *pKey, int nKey){
00111   int h = 0;
00112   const char *z = (const char *)pKey;
00113   while( nKey-- > 0 ){
00114     h = (h<<3) ^ h ^ *(z++);
00115   }
00116   return h & 0x7fffffff;
00117 }
00118 static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
00119   if( n1!=n2 ) return 1;
00120   return memcmp(pKey1,pKey2,n1);
00121 }
00122 
00123 /*
00124 ** Return a pointer to the appropriate hash function given the key class.
00125 **
00126 ** The C syntax in this function definition may be unfamilar to some 
00127 ** programmers, so we provide the following additional explanation:
00128 **
00129 ** The name of the function is "hashFunction".  The function takes a
00130 ** single parameter "keyClass".  The return value of hashFunction()
00131 ** is a pointer to another function.  Specifically, the return value
00132 ** of hashFunction() is a pointer to a function that takes two parameters
00133 ** with types "const void*" and "int" and returns an "int".
00134 */
00135 static int (*hashFunction(int keyClass))(const void*,int){
00136   if( keyClass==FTS1_HASH_STRING ){
00137     return &strHash;
00138   }else{
00139     assert( keyClass==FTS1_HASH_BINARY );
00140     return &binHash;
00141   }
00142 }
00143 
00144 /*
00145 ** Return a pointer to the appropriate hash function given the key class.
00146 **
00147 ** For help in interpreted the obscure C code in the function definition,
00148 ** see the header comment on the previous function.
00149 */
00150 static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
00151   if( keyClass==FTS1_HASH_STRING ){
00152     return &strCompare;
00153   }else{
00154     assert( keyClass==FTS1_HASH_BINARY );
00155     return &binCompare;
00156   }
00157 }
00158 
00159 /* Link an element into the hash table
00160 */
00161 static void insertElement(
00162   fts1Hash *pH,            /* The complete hash table */
00163   struct _fts1ht *pEntry,  /* The entry into which pNew is inserted */
00164   fts1HashElem *pNew       /* The element to be inserted */
00165 ){
00166   fts1HashElem *pHead;     /* First element already in pEntry */
00167   pHead = pEntry->chain;
00168   if( pHead ){
00169     pNew->next = pHead;
00170     pNew->prev = pHead->prev;
00171     if( pHead->prev ){ pHead->prev->next = pNew; }
00172     else             { pH->first = pNew; }
00173     pHead->prev = pNew;
00174   }else{
00175     pNew->next = pH->first;
00176     if( pH->first ){ pH->first->prev = pNew; }
00177     pNew->prev = 0;
00178     pH->first = pNew;
00179   }
00180   pEntry->count++;
00181   pEntry->chain = pNew;
00182 }
00183 
00184 
00185 /* Resize the hash table so that it cantains "new_size" buckets.
00186 ** "new_size" must be a power of 2.  The hash table might fail 
00187 ** to resize if sqliteMalloc() fails.
00188 */
00189 static void rehash(fts1Hash *pH, int new_size){
00190   struct _fts1ht *new_ht;          /* The new hash table */
00191   fts1HashElem *elem, *next_elem;  /* For looping over existing elements */
00192   int (*xHash)(const void*,int);   /* The hash function */
00193 
00194   assert( (new_size & (new_size-1))==0 );
00195   new_ht = (struct _fts1ht *)pH->xMalloc( new_size*sizeof(struct _fts1ht) );
00196   if( new_ht==0 ) return;
00197   if( pH->ht ) pH->xFree(pH->ht);
00198   pH->ht = new_ht;
00199   pH->htsize = new_size;
00200   xHash = hashFunction(pH->keyClass);
00201   for(elem=pH->first, pH->first=0; elem; elem = next_elem){
00202     int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
00203     next_elem = elem->next;
00204     insertElement(pH, &new_ht[h], elem);
00205   }
00206 }
00207 
00208 /* This function (for internal use only) locates an element in an
00209 ** hash table that matches the given key.  The hash for this key has
00210 ** already been computed and is passed as the 4th parameter.
00211 */
00212 static fts1HashElem *findElementGivenHash(
00213   const fts1Hash *pH, /* The pH to be searched */
00214   const void *pKey,   /* The key we are searching for */
00215   int nKey,
00216   int h               /* The hash for this key. */
00217 ){
00218   fts1HashElem *elem;            /* Used to loop thru the element list */
00219   int count;                     /* Number of elements left to test */
00220   int (*xCompare)(const void*,int,const void*,int);  /* comparison function */
00221 
00222   if( pH->ht ){
00223     struct _fts1ht *pEntry = &pH->ht[h];
00224     elem = pEntry->chain;
00225     count = pEntry->count;
00226     xCompare = compareFunction(pH->keyClass);
00227     while( count-- && elem ){
00228       if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ 
00229         return elem;
00230       }
00231       elem = elem->next;
00232     }
00233   }
00234   return 0;
00235 }
00236 
00237 /* Remove a single entry from the hash table given a pointer to that
00238 ** element and a hash on the element's key.
00239 */
00240 static void removeElementGivenHash(
00241   fts1Hash *pH,         /* The pH containing "elem" */
00242   fts1HashElem* elem,   /* The element to be removed from the pH */
00243   int h                 /* Hash value for the element */
00244 ){
00245   struct _fts1ht *pEntry;
00246   if( elem->prev ){
00247     elem->prev->next = elem->next; 
00248   }else{
00249     pH->first = elem->next;
00250   }
00251   if( elem->next ){
00252     elem->next->prev = elem->prev;
00253   }
00254   pEntry = &pH->ht[h];
00255   if( pEntry->chain==elem ){
00256     pEntry->chain = elem->next;
00257   }
00258   pEntry->count--;
00259   if( pEntry->count<=0 ){
00260     pEntry->chain = 0;
00261   }
00262   if( pH->copyKey && elem->pKey ){
00263     pH->xFree(elem->pKey);
00264   }
00265   pH->xFree( elem );
00266   pH->count--;
00267   if( pH->count<=0 ){
00268     assert( pH->first==0 );
00269     assert( pH->count==0 );
00270     fts1HashClear(pH);
00271   }
00272 }
00273 
00274 /* Attempt to locate an element of the hash table pH with a key
00275 ** that matches pKey,nKey.  Return the data for this element if it is
00276 ** found, or NULL if there is no match.
00277 */
00278 void *sqlite3Fts1HashFind(const fts1Hash *pH, const void *pKey, int nKey){
00279   int h;                 /* A hash on key */
00280   fts1HashElem *elem;    /* The element that matches key */
00281   int (*xHash)(const void*,int);  /* The hash function */
00282 
00283   if( pH==0 || pH->ht==0 ) return 0;
00284   xHash = hashFunction(pH->keyClass);
00285   assert( xHash!=0 );
00286   h = (*xHash)(pKey,nKey);
00287   assert( (pH->htsize & (pH->htsize-1))==0 );
00288   elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
00289   return elem ? elem->data : 0;
00290 }
00291 
00292 /* Insert an element into the hash table pH.  The key is pKey,nKey
00293 ** and the data is "data".
00294 **
00295 ** If no element exists with a matching key, then a new
00296 ** element is created.  A copy of the key is made if the copyKey
00297 ** flag is set.  NULL is returned.
00298 **
00299 ** If another element already exists with the same key, then the
00300 ** new data replaces the old data and the old data is returned.
00301 ** The key is not copied in this instance.  If a malloc fails, then
00302 ** the new data is returned and the hash table is unchanged.
00303 **
00304 ** If the "data" parameter to this function is NULL, then the
00305 ** element corresponding to "key" is removed from the hash table.
00306 */
00307 void *sqlite3Fts1HashInsert(
00308   fts1Hash *pH,        /* The hash table to insert into */
00309   const void *pKey,    /* The key */
00310   int nKey,            /* Number of bytes in the key */
00311   void *data           /* The data */
00312 ){
00313   int hraw;                 /* Raw hash value of the key */
00314   int h;                    /* the hash of the key modulo hash table size */
00315   fts1HashElem *elem;       /* Used to loop thru the element list */
00316   fts1HashElem *new_elem;   /* New element added to the pH */
00317   int (*xHash)(const void*,int);  /* The hash function */
00318 
00319   assert( pH!=0 );
00320   xHash = hashFunction(pH->keyClass);
00321   assert( xHash!=0 );
00322   hraw = (*xHash)(pKey, nKey);
00323   assert( (pH->htsize & (pH->htsize-1))==0 );
00324   h = hraw & (pH->htsize-1);
00325   elem = findElementGivenHash(pH,pKey,nKey,h);
00326   if( elem ){
00327     void *old_data = elem->data;
00328     if( data==0 ){
00329       removeElementGivenHash(pH,elem,h);
00330     }else{
00331       elem->data = data;
00332     }
00333     return old_data;
00334   }
00335   if( data==0 ) return 0;
00336   new_elem = (fts1HashElem*)pH->xMalloc( sizeof(fts1HashElem) );
00337   if( new_elem==0 ) return data;
00338   if( pH->copyKey && pKey!=0 ){
00339     new_elem->pKey = pH->xMalloc( nKey );
00340     if( new_elem->pKey==0 ){
00341       pH->xFree(new_elem);
00342       return data;
00343     }
00344     memcpy((void*)new_elem->pKey, pKey, nKey);
00345   }else{
00346     new_elem->pKey = (void*)pKey;
00347   }
00348   new_elem->nKey = nKey;
00349   pH->count++;
00350   if( pH->htsize==0 ){
00351     rehash(pH,8);
00352     if( pH->htsize==0 ){
00353       pH->count = 0;
00354       pH->xFree(new_elem);
00355       return data;
00356     }
00357   }
00358   if( pH->count > pH->htsize ){
00359     rehash(pH,pH->htsize*2);
00360   }
00361   assert( pH->htsize>0 );
00362   assert( (pH->htsize & (pH->htsize-1))==0 );
00363   h = hraw & (pH->htsize-1);
00364   insertElement(pH, &pH->ht[h], new_elem);
00365   new_elem->data = data;
00366   return 0;
00367 }
00368 
00369 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS1) */

ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:53 2011 by Doxygen 1.6.1