xref: /illumos-gate/usr/src/lib/libsqlite/src/hash.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate ** 2001 September 22
37c478bd9Sstevel@tonic-gate **
47c478bd9Sstevel@tonic-gate ** The author disclaims copyright to this source code.  In place of
57c478bd9Sstevel@tonic-gate ** a legal notice, here is a blessing:
67c478bd9Sstevel@tonic-gate **
77c478bd9Sstevel@tonic-gate **    May you do good and not evil.
87c478bd9Sstevel@tonic-gate **    May you find forgiveness for yourself and forgive others.
97c478bd9Sstevel@tonic-gate **    May you share freely, never taking more than you give.
107c478bd9Sstevel@tonic-gate **
117c478bd9Sstevel@tonic-gate *************************************************************************
127c478bd9Sstevel@tonic-gate ** This is the implementation of generic hash-tables
137c478bd9Sstevel@tonic-gate ** used in SQLite.
147c478bd9Sstevel@tonic-gate **
157c478bd9Sstevel@tonic-gate ** $Id: hash.c,v 1.11 2004/01/08 02:17:33 drh Exp $
167c478bd9Sstevel@tonic-gate */
177c478bd9Sstevel@tonic-gate #include "sqliteInt.h"
187c478bd9Sstevel@tonic-gate #include <assert.h>
197c478bd9Sstevel@tonic-gate 
207c478bd9Sstevel@tonic-gate /* Turn bulk memory into a hash table object by initializing the
217c478bd9Sstevel@tonic-gate ** fields of the Hash structure.
227c478bd9Sstevel@tonic-gate **
237c478bd9Sstevel@tonic-gate ** "new" is a pointer to the hash table that is to be initialized.
247c478bd9Sstevel@tonic-gate ** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER,
25*1da57d55SToomas Soome ** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING.  The value of keyClass
267c478bd9Sstevel@tonic-gate ** determines what kind of key the hash table will use.  "copyKey" is
277c478bd9Sstevel@tonic-gate ** true if the hash table should make its own private copy of keys and
287c478bd9Sstevel@tonic-gate ** false if it should just use the supplied pointer.  CopyKey only makes
297c478bd9Sstevel@tonic-gate ** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored
307c478bd9Sstevel@tonic-gate ** for other key classes.
317c478bd9Sstevel@tonic-gate */
sqliteHashInit(Hash * new,int keyClass,int copyKey)327c478bd9Sstevel@tonic-gate void sqliteHashInit(Hash *new, int keyClass, int copyKey){
337c478bd9Sstevel@tonic-gate   assert( new!=0 );
347c478bd9Sstevel@tonic-gate   assert( keyClass>=SQLITE_HASH_INT && keyClass<=SQLITE_HASH_BINARY );
357c478bd9Sstevel@tonic-gate   new->keyClass = keyClass;
367c478bd9Sstevel@tonic-gate   new->copyKey = copyKey &&
377c478bd9Sstevel@tonic-gate                 (keyClass==SQLITE_HASH_STRING || keyClass==SQLITE_HASH_BINARY);
387c478bd9Sstevel@tonic-gate   new->first = 0;
397c478bd9Sstevel@tonic-gate   new->count = 0;
407c478bd9Sstevel@tonic-gate   new->htsize = 0;
417c478bd9Sstevel@tonic-gate   new->ht = 0;
427c478bd9Sstevel@tonic-gate }
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /* Remove all entries from a hash table.  Reclaim all memory.
457c478bd9Sstevel@tonic-gate ** Call this routine to delete a hash table or to reset a hash table
467c478bd9Sstevel@tonic-gate ** to the empty state.
477c478bd9Sstevel@tonic-gate */
sqliteHashClear(Hash * pH)487c478bd9Sstevel@tonic-gate void sqliteHashClear(Hash *pH){
497c478bd9Sstevel@tonic-gate   HashElem *elem;         /* For looping over all elements of the table */
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate   assert( pH!=0 );
527c478bd9Sstevel@tonic-gate   elem = pH->first;
537c478bd9Sstevel@tonic-gate   pH->first = 0;
547c478bd9Sstevel@tonic-gate   if( pH->ht ) sqliteFree(pH->ht);
557c478bd9Sstevel@tonic-gate   pH->ht = 0;
567c478bd9Sstevel@tonic-gate   pH->htsize = 0;
577c478bd9Sstevel@tonic-gate   while( elem ){
587c478bd9Sstevel@tonic-gate     HashElem *next_elem = elem->next;
597c478bd9Sstevel@tonic-gate     if( pH->copyKey && elem->pKey ){
607c478bd9Sstevel@tonic-gate       sqliteFree(elem->pKey);
617c478bd9Sstevel@tonic-gate     }
627c478bd9Sstevel@tonic-gate     sqliteFree(elem);
637c478bd9Sstevel@tonic-gate     elem = next_elem;
647c478bd9Sstevel@tonic-gate   }
657c478bd9Sstevel@tonic-gate   pH->count = 0;
667c478bd9Sstevel@tonic-gate }
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate /*
697c478bd9Sstevel@tonic-gate ** Hash and comparison functions when the mode is SQLITE_HASH_INT
707c478bd9Sstevel@tonic-gate */
intHash(const void * pKey,int nKey)717c478bd9Sstevel@tonic-gate static int intHash(const void *pKey, int nKey){
727c478bd9Sstevel@tonic-gate   return nKey ^ (nKey<<8) ^ (nKey>>8);
737c478bd9Sstevel@tonic-gate }
intCompare(const void * pKey1,int n1,const void * pKey2,int n2)747c478bd9Sstevel@tonic-gate static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
757c478bd9Sstevel@tonic-gate   return n2 - n1;
767c478bd9Sstevel@tonic-gate }
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate #if 0 /* NOT USED */
797c478bd9Sstevel@tonic-gate /*
807c478bd9Sstevel@tonic-gate ** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
817c478bd9Sstevel@tonic-gate */
827c478bd9Sstevel@tonic-gate static int ptrHash(const void *pKey, int nKey){
837c478bd9Sstevel@tonic-gate   uptr x = Addr(pKey);
847c478bd9Sstevel@tonic-gate   return x ^ (x<<8) ^ (x>>8);
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
877c478bd9Sstevel@tonic-gate   if( pKey1==pKey2 ) return 0;
887c478bd9Sstevel@tonic-gate   if( pKey1<pKey2 ) return -1;
897c478bd9Sstevel@tonic-gate   return 1;
907c478bd9Sstevel@tonic-gate }
917c478bd9Sstevel@tonic-gate #endif
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate ** Hash and comparison functions when the mode is SQLITE_HASH_STRING
957c478bd9Sstevel@tonic-gate */
strHash(const void * pKey,int nKey)967c478bd9Sstevel@tonic-gate static int strHash(const void *pKey, int nKey){
97*1da57d55SToomas Soome   return sqliteHashNoCase((const char*)pKey, nKey);
987c478bd9Sstevel@tonic-gate }
strCompare(const void * pKey1,int n1,const void * pKey2,int n2)997c478bd9Sstevel@tonic-gate static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
1007c478bd9Sstevel@tonic-gate   if( n1!=n2 ) return n2-n1;
1017c478bd9Sstevel@tonic-gate   return sqliteStrNICmp((const char*)pKey1,(const char*)pKey2,n1);
1027c478bd9Sstevel@tonic-gate }
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate ** Hash and comparison functions when the mode is SQLITE_HASH_BINARY
1067c478bd9Sstevel@tonic-gate */
binHash(const void * pKey,int nKey)1077c478bd9Sstevel@tonic-gate static int binHash(const void *pKey, int nKey){
1087c478bd9Sstevel@tonic-gate   int h = 0;
1097c478bd9Sstevel@tonic-gate   const char *z = (const char *)pKey;
1107c478bd9Sstevel@tonic-gate   while( nKey-- > 0 ){
1117c478bd9Sstevel@tonic-gate     h = (h<<3) ^ h ^ *(z++);
1127c478bd9Sstevel@tonic-gate   }
1137c478bd9Sstevel@tonic-gate   return h & 0x7fffffff;
1147c478bd9Sstevel@tonic-gate }
binCompare(const void * pKey1,int n1,const void * pKey2,int n2)1157c478bd9Sstevel@tonic-gate static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
1167c478bd9Sstevel@tonic-gate   if( n1!=n2 ) return n2-n1;
1177c478bd9Sstevel@tonic-gate   return memcmp(pKey1,pKey2,n1);
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate ** Return a pointer to the appropriate hash function given the key class.
1227c478bd9Sstevel@tonic-gate **
123*1da57d55SToomas Soome ** The C syntax in this function definition may be unfamilar to some
1247c478bd9Sstevel@tonic-gate ** programmers, so we provide the following additional explanation:
1257c478bd9Sstevel@tonic-gate **
1267c478bd9Sstevel@tonic-gate ** The name of the function is "hashFunction".  The function takes a
1277c478bd9Sstevel@tonic-gate ** single parameter "keyClass".  The return value of hashFunction()
1287c478bd9Sstevel@tonic-gate ** is a pointer to another function.  Specifically, the return value
1297c478bd9Sstevel@tonic-gate ** of hashFunction() is a pointer to a function that takes two parameters
1307c478bd9Sstevel@tonic-gate ** with types "const void*" and "int" and returns an "int".
1317c478bd9Sstevel@tonic-gate */
hashFunction(int keyClass)1327c478bd9Sstevel@tonic-gate static int (*hashFunction(int keyClass))(const void*,int){
1337c478bd9Sstevel@tonic-gate   switch( keyClass ){
1347c478bd9Sstevel@tonic-gate     case SQLITE_HASH_INT:     return &intHash;
1357c478bd9Sstevel@tonic-gate     /* case SQLITE_HASH_POINTER: return &ptrHash; // NOT USED */
1367c478bd9Sstevel@tonic-gate     case SQLITE_HASH_STRING:  return &strHash;
1377c478bd9Sstevel@tonic-gate     case SQLITE_HASH_BINARY:  return &binHash;;
1387c478bd9Sstevel@tonic-gate     default: break;
1397c478bd9Sstevel@tonic-gate   }
1407c478bd9Sstevel@tonic-gate   return 0;
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate /*
1447c478bd9Sstevel@tonic-gate ** Return a pointer to the appropriate hash function given the key class.
1457c478bd9Sstevel@tonic-gate **
1467c478bd9Sstevel@tonic-gate ** For help in interpreted the obscure C code in the function definition,
1477c478bd9Sstevel@tonic-gate ** see the header comment on the previous function.
1487c478bd9Sstevel@tonic-gate */
compareFunction(int keyClass)1497c478bd9Sstevel@tonic-gate static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
1507c478bd9Sstevel@tonic-gate   switch( keyClass ){
1517c478bd9Sstevel@tonic-gate     case SQLITE_HASH_INT:     return &intCompare;
1527c478bd9Sstevel@tonic-gate     /* case SQLITE_HASH_POINTER: return &ptrCompare; // NOT USED */
1537c478bd9Sstevel@tonic-gate     case SQLITE_HASH_STRING:  return &strCompare;
1547c478bd9Sstevel@tonic-gate     case SQLITE_HASH_BINARY:  return &binCompare;
1557c478bd9Sstevel@tonic-gate     default: break;
1567c478bd9Sstevel@tonic-gate   }
1577c478bd9Sstevel@tonic-gate   return 0;
1587c478bd9Sstevel@tonic-gate }
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate /* Resize the hash table so that it cantains "new_size" buckets.
162*1da57d55SToomas Soome ** "new_size" must be a power of 2.  The hash table might fail
1637c478bd9Sstevel@tonic-gate ** to resize if sqliteMalloc() fails.
1647c478bd9Sstevel@tonic-gate */
rehash(Hash * pH,int new_size)1657c478bd9Sstevel@tonic-gate static void rehash(Hash *pH, int new_size){
1667c478bd9Sstevel@tonic-gate   struct _ht *new_ht;            /* The new hash table */
1677c478bd9Sstevel@tonic-gate   HashElem *elem, *next_elem;    /* For looping over existing elements */
1687c478bd9Sstevel@tonic-gate   HashElem *x;                   /* Element being copied to new hash table */
1697c478bd9Sstevel@tonic-gate   int (*xHash)(const void*,int); /* The hash function */
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate   assert( (new_size & (new_size-1))==0 );
1727c478bd9Sstevel@tonic-gate   new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) );
1737c478bd9Sstevel@tonic-gate   if( new_ht==0 ) return;
1747c478bd9Sstevel@tonic-gate   if( pH->ht ) sqliteFree(pH->ht);
1757c478bd9Sstevel@tonic-gate   pH->ht = new_ht;
1767c478bd9Sstevel@tonic-gate   pH->htsize = new_size;
1777c478bd9Sstevel@tonic-gate   xHash = hashFunction(pH->keyClass);
1787c478bd9Sstevel@tonic-gate   for(elem=pH->first, pH->first=0; elem; elem = next_elem){
1797c478bd9Sstevel@tonic-gate     int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
1807c478bd9Sstevel@tonic-gate     next_elem = elem->next;
1817c478bd9Sstevel@tonic-gate     x = new_ht[h].chain;
1827c478bd9Sstevel@tonic-gate     if( x ){
1837c478bd9Sstevel@tonic-gate       elem->next = x;
1847c478bd9Sstevel@tonic-gate       elem->prev = x->prev;
1857c478bd9Sstevel@tonic-gate       if( x->prev ) x->prev->next = elem;
1867c478bd9Sstevel@tonic-gate       else          pH->first = elem;
1877c478bd9Sstevel@tonic-gate       x->prev = elem;
1887c478bd9Sstevel@tonic-gate     }else{
1897c478bd9Sstevel@tonic-gate       elem->next = pH->first;
1907c478bd9Sstevel@tonic-gate       if( pH->first ) pH->first->prev = elem;
1917c478bd9Sstevel@tonic-gate       elem->prev = 0;
1927c478bd9Sstevel@tonic-gate       pH->first = elem;
1937c478bd9Sstevel@tonic-gate     }
1947c478bd9Sstevel@tonic-gate     new_ht[h].chain = elem;
1957c478bd9Sstevel@tonic-gate     new_ht[h].count++;
1967c478bd9Sstevel@tonic-gate   }
1977c478bd9Sstevel@tonic-gate }
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /* This function (for internal use only) locates an element in an
2007c478bd9Sstevel@tonic-gate ** hash table that matches the given key.  The hash for this key has
2017c478bd9Sstevel@tonic-gate ** already been computed and is passed as the 4th parameter.
2027c478bd9Sstevel@tonic-gate */
findElementGivenHash(const Hash * pH,const void * pKey,int nKey,int h)2037c478bd9Sstevel@tonic-gate static HashElem *findElementGivenHash(
2047c478bd9Sstevel@tonic-gate   const Hash *pH,     /* The pH to be searched */
2057c478bd9Sstevel@tonic-gate   const void *pKey,   /* The key we are searching for */
2067c478bd9Sstevel@tonic-gate   int nKey,
2077c478bd9Sstevel@tonic-gate   int h               /* The hash for this key. */
2087c478bd9Sstevel@tonic-gate ){
2097c478bd9Sstevel@tonic-gate   HashElem *elem;                /* Used to loop thru the element list */
2107c478bd9Sstevel@tonic-gate   int count;                     /* Number of elements left to test */
2117c478bd9Sstevel@tonic-gate   int (*xCompare)(const void*,int,const void*,int);  /* comparison function */
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate   if( pH->ht ){
2147c478bd9Sstevel@tonic-gate     elem = pH->ht[h].chain;
2157c478bd9Sstevel@tonic-gate     count = pH->ht[h].count;
2167c478bd9Sstevel@tonic-gate     xCompare = compareFunction(pH->keyClass);
2177c478bd9Sstevel@tonic-gate     while( count-- && elem ){
218*1da57d55SToomas Soome       if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
2197c478bd9Sstevel@tonic-gate         return elem;
2207c478bd9Sstevel@tonic-gate       }
2217c478bd9Sstevel@tonic-gate       elem = elem->next;
2227c478bd9Sstevel@tonic-gate     }
2237c478bd9Sstevel@tonic-gate   }
2247c478bd9Sstevel@tonic-gate   return 0;
2257c478bd9Sstevel@tonic-gate }
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate /* Remove a single entry from the hash table given a pointer to that
2287c478bd9Sstevel@tonic-gate ** element and a hash on the element's key.
2297c478bd9Sstevel@tonic-gate */
removeElementGivenHash(Hash * pH,HashElem * elem,int h)2307c478bd9Sstevel@tonic-gate static void removeElementGivenHash(
2317c478bd9Sstevel@tonic-gate   Hash *pH,         /* The pH containing "elem" */
2327c478bd9Sstevel@tonic-gate   HashElem* elem,   /* The element to be removed from the pH */
2337c478bd9Sstevel@tonic-gate   int h             /* Hash value for the element */
2347c478bd9Sstevel@tonic-gate ){
2357c478bd9Sstevel@tonic-gate   if( elem->prev ){
236*1da57d55SToomas Soome     elem->prev->next = elem->next;
2377c478bd9Sstevel@tonic-gate   }else{
2387c478bd9Sstevel@tonic-gate     pH->first = elem->next;
2397c478bd9Sstevel@tonic-gate   }
2407c478bd9Sstevel@tonic-gate   if( elem->next ){
2417c478bd9Sstevel@tonic-gate     elem->next->prev = elem->prev;
2427c478bd9Sstevel@tonic-gate   }
2437c478bd9Sstevel@tonic-gate   if( pH->ht[h].chain==elem ){
2447c478bd9Sstevel@tonic-gate     pH->ht[h].chain = elem->next;
2457c478bd9Sstevel@tonic-gate   }
2467c478bd9Sstevel@tonic-gate   pH->ht[h].count--;
2477c478bd9Sstevel@tonic-gate   if( pH->ht[h].count<=0 ){
2487c478bd9Sstevel@tonic-gate     pH->ht[h].chain = 0;
2497c478bd9Sstevel@tonic-gate   }
2507c478bd9Sstevel@tonic-gate   if( pH->copyKey && elem->pKey ){
2517c478bd9Sstevel@tonic-gate     sqliteFree(elem->pKey);
2527c478bd9Sstevel@tonic-gate   }
2537c478bd9Sstevel@tonic-gate   sqliteFree( elem );
2547c478bd9Sstevel@tonic-gate   pH->count--;
2557c478bd9Sstevel@tonic-gate }
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate /* Attempt to locate an element of the hash table pH with a key
2587c478bd9Sstevel@tonic-gate ** that matches pKey,nKey.  Return the data for this element if it is
2597c478bd9Sstevel@tonic-gate ** found, or NULL if there is no match.
2607c478bd9Sstevel@tonic-gate */
sqliteHashFind(const Hash * pH,const void * pKey,int nKey)2617c478bd9Sstevel@tonic-gate void *sqliteHashFind(const Hash *pH, const void *pKey, int nKey){
2627c478bd9Sstevel@tonic-gate   int h;             /* A hash on key */
2637c478bd9Sstevel@tonic-gate   HashElem *elem;    /* The element that matches key */
2647c478bd9Sstevel@tonic-gate   int (*xHash)(const void*,int);  /* The hash function */
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate   if( pH==0 || pH->ht==0 ) return 0;
2677c478bd9Sstevel@tonic-gate   xHash = hashFunction(pH->keyClass);
2687c478bd9Sstevel@tonic-gate   assert( xHash!=0 );
2697c478bd9Sstevel@tonic-gate   h = (*xHash)(pKey,nKey);
2707c478bd9Sstevel@tonic-gate   assert( (pH->htsize & (pH->htsize-1))==0 );
2717c478bd9Sstevel@tonic-gate   elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
2727c478bd9Sstevel@tonic-gate   return elem ? elem->data : 0;
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate /* Insert an element into the hash table pH.  The key is pKey,nKey
2767c478bd9Sstevel@tonic-gate ** and the data is "data".
2777c478bd9Sstevel@tonic-gate **
2787c478bd9Sstevel@tonic-gate ** If no element exists with a matching key, then a new
2797c478bd9Sstevel@tonic-gate ** element is created.  A copy of the key is made if the copyKey
2807c478bd9Sstevel@tonic-gate ** flag is set.  NULL is returned.
2817c478bd9Sstevel@tonic-gate **
2827c478bd9Sstevel@tonic-gate ** If another element already exists with the same key, then the
2837c478bd9Sstevel@tonic-gate ** new data replaces the old data and the old data is returned.
2847c478bd9Sstevel@tonic-gate ** The key is not copied in this instance.  If a malloc fails, then
2857c478bd9Sstevel@tonic-gate ** the new data is returned and the hash table is unchanged.
2867c478bd9Sstevel@tonic-gate **
2877c478bd9Sstevel@tonic-gate ** If the "data" parameter to this function is NULL, then the
2887c478bd9Sstevel@tonic-gate ** element corresponding to "key" is removed from the hash table.
2897c478bd9Sstevel@tonic-gate */
sqliteHashInsert(Hash * pH,const void * pKey,int nKey,void * data)2907c478bd9Sstevel@tonic-gate void *sqliteHashInsert(Hash *pH, const void *pKey, int nKey, void *data){
2917c478bd9Sstevel@tonic-gate   int hraw;             /* Raw hash value of the key */
2927c478bd9Sstevel@tonic-gate   int h;                /* the hash of the key modulo hash table size */
2937c478bd9Sstevel@tonic-gate   HashElem *elem;       /* Used to loop thru the element list */
2947c478bd9Sstevel@tonic-gate   HashElem *new_elem;   /* New element added to the pH */
2957c478bd9Sstevel@tonic-gate   int (*xHash)(const void*,int);  /* The hash function */
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate   assert( pH!=0 );
2987c478bd9Sstevel@tonic-gate   xHash = hashFunction(pH->keyClass);
2997c478bd9Sstevel@tonic-gate   assert( xHash!=0 );
3007c478bd9Sstevel@tonic-gate   hraw = (*xHash)(pKey, nKey);
3017c478bd9Sstevel@tonic-gate   assert( (pH->htsize & (pH->htsize-1))==0 );
3027c478bd9Sstevel@tonic-gate   h = hraw & (pH->htsize-1);
3037c478bd9Sstevel@tonic-gate   elem = findElementGivenHash(pH,pKey,nKey,h);
3047c478bd9Sstevel@tonic-gate   if( elem ){
3057c478bd9Sstevel@tonic-gate     void *old_data = elem->data;
3067c478bd9Sstevel@tonic-gate     if( data==0 ){
3077c478bd9Sstevel@tonic-gate       removeElementGivenHash(pH,elem,h);
3087c478bd9Sstevel@tonic-gate     }else{
3097c478bd9Sstevel@tonic-gate       elem->data = data;
3107c478bd9Sstevel@tonic-gate     }
3117c478bd9Sstevel@tonic-gate     return old_data;
3127c478bd9Sstevel@tonic-gate   }
3137c478bd9Sstevel@tonic-gate   if( data==0 ) return 0;
3147c478bd9Sstevel@tonic-gate   new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) );
3157c478bd9Sstevel@tonic-gate   if( new_elem==0 ) return data;
3167c478bd9Sstevel@tonic-gate   if( pH->copyKey && pKey!=0 ){
3177c478bd9Sstevel@tonic-gate     new_elem->pKey = sqliteMallocRaw( nKey );
3187c478bd9Sstevel@tonic-gate     if( new_elem->pKey==0 ){
3197c478bd9Sstevel@tonic-gate       sqliteFree(new_elem);
3207c478bd9Sstevel@tonic-gate       return data;
3217c478bd9Sstevel@tonic-gate     }
3227c478bd9Sstevel@tonic-gate     memcpy((void*)new_elem->pKey, pKey, nKey);
3237c478bd9Sstevel@tonic-gate   }else{
3247c478bd9Sstevel@tonic-gate     new_elem->pKey = (void*)pKey;
3257c478bd9Sstevel@tonic-gate   }
3267c478bd9Sstevel@tonic-gate   new_elem->nKey = nKey;
3277c478bd9Sstevel@tonic-gate   pH->count++;
3287c478bd9Sstevel@tonic-gate   if( pH->htsize==0 ) rehash(pH,8);
3297c478bd9Sstevel@tonic-gate   if( pH->htsize==0 ){
3307c478bd9Sstevel@tonic-gate     pH->count = 0;
3317c478bd9Sstevel@tonic-gate     sqliteFree(new_elem);
3327c478bd9Sstevel@tonic-gate     return data;
3337c478bd9Sstevel@tonic-gate   }
3347c478bd9Sstevel@tonic-gate   if( pH->count > pH->htsize ){
3357c478bd9Sstevel@tonic-gate     rehash(pH,pH->htsize*2);
3367c478bd9Sstevel@tonic-gate   }
3377c478bd9Sstevel@tonic-gate   assert( (pH->htsize & (pH->htsize-1))==0 );
3387c478bd9Sstevel@tonic-gate   h = hraw & (pH->htsize-1);
3397c478bd9Sstevel@tonic-gate   elem = pH->ht[h].chain;
3407c478bd9Sstevel@tonic-gate   if( elem ){
3417c478bd9Sstevel@tonic-gate     new_elem->next = elem;
3427c478bd9Sstevel@tonic-gate     new_elem->prev = elem->prev;
3437c478bd9Sstevel@tonic-gate     if( elem->prev ){ elem->prev->next = new_elem; }
3447c478bd9Sstevel@tonic-gate     else            { pH->first = new_elem; }
3457c478bd9Sstevel@tonic-gate     elem->prev = new_elem;
3467c478bd9Sstevel@tonic-gate   }else{
3477c478bd9Sstevel@tonic-gate     new_elem->next = pH->first;
3487c478bd9Sstevel@tonic-gate     new_elem->prev = 0;
3497c478bd9Sstevel@tonic-gate     if( pH->first ){ pH->first->prev = new_elem; }
3507c478bd9Sstevel@tonic-gate     pH->first = new_elem;
3517c478bd9Sstevel@tonic-gate   }
3527c478bd9Sstevel@tonic-gate   pH->ht[h].count++;
3537c478bd9Sstevel@tonic-gate   pH->ht[h].chain = new_elem;
3547c478bd9Sstevel@tonic-gate   new_elem->data = data;
3557c478bd9Sstevel@tonic-gate   return 0;
3567c478bd9Sstevel@tonic-gate }
357