blob: 26d88e8f919daaf4ae7dbb90e45fa18e24bfcfd9 [file] [log] [blame]
drhbeae3192001-09-22 18:12:08 +00001/*
2** 2001 September 22
3**
4** The author disclaims copyright to this source code. In place of
5** a legal notice, here is a blessing:
6**
7** May you do good and not evil.
8** May you find forgiveness for yourself and forgive others.
9** May you share freely, never taking more than you give.
10**
11*************************************************************************
12** This is the implementation of generic hash-tables
13** used in SQLite.
14**
drh6d4abfb2001-10-22 02:58:08 +000015** $Id: hash.c,v 1.3 2001/10/22 02:58:10 drh Exp $
drhbeae3192001-09-22 18:12:08 +000016*/
17#include "sqliteInt.h"
18#include <assert.h>
19
20/* Turn bulk memory into a hash table object by initializing the
21** fields of the Hash structure.
22*/
23void sqliteHashInit(Hash *new, int keyClass, int copyKey){
24 assert( new!=0 );
25 assert( keyClass>=SQLITE_HASH_INT && keyClass<=SQLITE_HASH_BINARY );
26 new->keyClass = keyClass;
27 new->copyKey = copyKey &&
28 (keyClass==SQLITE_HASH_STRING || keyClass==SQLITE_HASH_BINARY);
29 new->first = 0;
30 new->count = 0;
31 new->htsize = 0;
32 new->ht = 0;
33}
34
35/* Remove all entries from a hash table. Reclaim all memory.
36*/
37void sqliteHashClear(Hash *pH){
38 HashElem *elem; /* For looping over all elements of the table */
39
40 assert( pH!=0 );
41 elem = pH->first;
42 pH->first = 0;
43 if( pH->ht ) sqliteFree(pH->ht);
44 pH->ht = 0;
45 pH->htsize = 0;
46 while( elem ){
47 HashElem *next_elem = elem->next;
48 if( pH->copyKey && elem->pKey ){
49 sqliteFree(elem->pKey);
50 }
51 sqliteFree(elem);
52 elem = next_elem;
53 }
54 pH->count = 0;
55}
56
57/*
58** Hash and comparison functions when the mode is SQLITE_HASH_INT
59*/
60static int intHash(const void *pKey, int nKey){
61 return nKey ^ (nKey<<8) ^ (nKey>>8);
62}
63static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
64 return n2 - n1;
65}
66
67/*
68** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
69*/
70static int ptrHash(const void *pKey, int nKey){
71 nKey = (int)pKey;
72 return nKey ^ (nKey<<8) ^ (nKey>>8);
73}
74static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
75 return ((int)pKey2) - (int)pKey1;
76}
77
78/*
79** Hash and comparison functions when the mode is SQLITE_HASH_STRING
80*/
81static int strHash(const void *pKey, int nKey){
82 return sqliteHashNoCase((const char*)pKey, nKey);
83}
84static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
85 if( n1!=n2 ) return n2-n1;
86 return sqliteStrNICmp((const char*)pKey1,(const char*)pKey2,n1);
87}
88
89/*
90** Hash and comparison functions when the mode is SQLITE_HASH_BINARY
91*/
92static int binHash(const void *pKey, int nKey){
93 int h = 0;
94 const char *z = (const char *)pKey;
95 while( nKey-- > 0 ){
96 h = (h<<3) ^ h ^ *(z++);
97 }
98 if( h<0 ) h = -h;
99 return h;
100}
101static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
102 if( n1!=n2 ) return n2-n1;
103 return memcmp(pKey1,pKey2,n1);
104}
105
106/*
107** Return a pointer to the appropriate hash function given the key class.
108*/
109static int (*hashFunction(int keyClass))(const void*,int){
110 switch( keyClass ){
111 case SQLITE_HASH_INT: return intHash;
112 case SQLITE_HASH_POINTER: return ptrHash;
113 case SQLITE_HASH_STRING: return strHash;
114 case SQLITE_HASH_BINARY: return binHash;;
115 default: break;
116 }
117 return 0;
118}
119
120/*
121** Return a pointer to the appropriate hash function given the key class.
122*/
123static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
124 switch( keyClass ){
125 case SQLITE_HASH_INT: return intCompare;
126 case SQLITE_HASH_POINTER: return ptrCompare;
127 case SQLITE_HASH_STRING: return strCompare;
128 case SQLITE_HASH_BINARY: return binCompare;
129 default: break;
130 }
131 return 0;
132}
133
134
135/* Resize the hash table. new_size must be a power of 2.
136** The hash table might fail to resize if sqliteMalloc() fails.
137*/
138static void rehash(Hash *pH, int new_size){
139 struct _ht *new_ht; /* The new hash table */
140 HashElem *elem, *next_elem; /* For looping over existing elements */
141 HashElem *x; /* Element being copied to new hash table */
142 int (*xHash)(const void*,int); /* The hash function */
143
144 assert( (new_size & (new_size-1))==0 );
145 new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) );
146 if( new_ht==0 ) return;
147 if( pH->ht ) sqliteFree(pH->ht);
148 pH->ht = new_ht;
149 pH->htsize = new_size;
150 xHash = hashFunction(pH->keyClass);
151 for(elem=pH->first, pH->first=0; elem; elem = next_elem){
152 int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
153 next_elem = elem->next;
154 x = new_ht[h].chain;
155 if( x ){
156 elem->next = x;
157 elem->prev = x->prev;
158 if( x->prev ) x->prev->next = elem;
159 else pH->first = elem;
160 x->prev = elem;
161 }else{
162 elem->next = pH->first;
163 if( pH->first ) pH->first->prev = elem;
164 elem->prev = 0;
165 pH->first = elem;
166 }
167 new_ht[h].chain = elem;
168 new_ht[h].count++;
169 }
170}
171
172/* This function (for internal use only) locates an element in an
173** pH that matches the given key. The hash for this key has
174** already been computed and is passed as the 3rd parameter.
175*/
176static HashElem *findElementGivenHash(
177 const Hash *pH, /* The pH to be searched */
178 const void *pKey, /* The key we are searching for */
179 int nKey,
180 int h /* The hash for this key. */
181){
182 HashElem *elem; /* Used to loop thru the element list */
183 int count; /* Number of elements left to test */
184 int (*xCompare)(const void*,int,const void*,int); /* comparison function */
185
186 if( pH->ht ){
187 elem = pH->ht[h].chain;
188 count = pH->ht[h].count;
189 xCompare = compareFunction(pH->keyClass);
190 while( count-- && elem ){
191 if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
192 return elem;
193 }
194 elem = elem->next;
195 }
196 }
197 return 0;
198}
199
drh81a20f22001-10-12 17:30:04 +0000200/* Remove a single entry from the hash table given a pointer to that
drhbeae3192001-09-22 18:12:08 +0000201** element and a hash on the element's key.
202*/
203static void removeElementGivenHash(
204 Hash *pH, /* The pH containing "elem" */
205 HashElem* elem, /* The element to be removed from the pH */
206 int h /* Hash value for the element */
207){
208 if( elem->prev ){
209 elem->prev->next = elem->next;
210 }else{
211 pH->first = elem->next;
212 }
213 if( elem->next ){
214 elem->next->prev = elem->prev;
215 }
216 if( pH->ht[h].chain==elem ){
217 pH->ht[h].chain = elem->next;
218 }
219 pH->ht[h].count--;
220 if( pH->ht[h].count<=0 ){
221 pH->ht[h].chain = 0;
222 }
223 if( pH->copyKey && elem->pKey ){
224 sqliteFree(elem->pKey);
225 }
226 sqliteFree( elem );
227 pH->count--;
228}
229
230/* Attempt to locate an element of the associative pH with a key
drh81a20f22001-10-12 17:30:04 +0000231** that matches pKey,nKey. Return the data for this element if it is
drhbeae3192001-09-22 18:12:08 +0000232** found, or NULL if no match is found.
233*/
234void *sqliteHashFind(const Hash *pH, const void *pKey, int nKey){
235 int h; /* A hash on key */
236 HashElem *elem; /* The element that matches key */
237 int (*xHash)(const void*,int); /* The hash function */
238
239 if( pH==0 || pH->ht==0 ) return 0;
240 xHash = hashFunction(pH->keyClass);
241 assert( xHash!=0 );
242 h = (*xHash)(pKey,nKey);
243 assert( (pH->htsize & (pH->htsize-1))==0 );
244 elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
245 return elem ? elem->data : 0;
246}
247
drh81a20f22001-10-12 17:30:04 +0000248/* Insert an element into the hash table pH. The key is pKey,nKey
249** and the data is "data".
drhbeae3192001-09-22 18:12:08 +0000250**
drh81a20f22001-10-12 17:30:04 +0000251** If no element exists with a matching key, then a new
252** element is created. A copy of the key is made if the copyKey
253** flag is set. NULL is returned.
drhbeae3192001-09-22 18:12:08 +0000254**
255** If another element already exists with the same key, then the
256** new data replaces the old data and the old data is returned.
drh6d4abfb2001-10-22 02:58:08 +0000257** The key is not copied in this instance. If a malloc fails, then
258** new data is returned.
drhbeae3192001-09-22 18:12:08 +0000259**
260** If the "data" parameter to this function is NULL, then the
drh81a20f22001-10-12 17:30:04 +0000261** element corresponding to "key" is removed from the hash table.
drhbeae3192001-09-22 18:12:08 +0000262*/
263void *sqliteHashInsert(Hash *pH, void *pKey, int nKey, void *data){
264 int hraw; /* Raw hash value of the key */
265 int h; /* the hash of the key modulo hash table size */
266 HashElem *elem; /* Used to loop thru the element list */
267 HashElem *new_elem; /* New element added to the pH */
268 int (*xHash)(const void*,int); /* The hash function */
269
270 assert( pH!=0 );
271 xHash = hashFunction(pH->keyClass);
272 assert( xHash!=0 );
273 hraw = (*xHash)(pKey, nKey);
274 assert( (pH->htsize & (pH->htsize-1))==0 );
275 h = hraw & (pH->htsize-1);
276 elem = findElementGivenHash(pH,pKey,nKey,h);
277 if( elem ){
278 void *old_data = elem->data;
279 if( data==0 ){
280 removeElementGivenHash(pH,elem,h);
281 }else{
282 elem->data = data;
283 }
284 return old_data;
285 }
286 if( data==0 ) return 0;
287 new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) );
drh6d4abfb2001-10-22 02:58:08 +0000288 if( new_elem==0 ) return data;
drhbeae3192001-09-22 18:12:08 +0000289 if( pH->copyKey && pKey!=0 ){
290 new_elem->pKey = sqliteMalloc( nKey );
291 if( new_elem->pKey==0 ){
292 sqliteFree(new_elem);
drh6d4abfb2001-10-22 02:58:08 +0000293 return data;
drhbeae3192001-09-22 18:12:08 +0000294 }
295 memcpy((void*)new_elem->pKey, pKey, nKey);
296 }else{
297 new_elem->pKey = pKey;
298 }
299 new_elem->nKey = nKey;
300 pH->count++;
301 if( pH->htsize==0 ) rehash(pH,8);
302 if( pH->htsize==0 ){
303 pH->count = 0;
304 sqliteFree(new_elem);
drh6d4abfb2001-10-22 02:58:08 +0000305 return data;
drhbeae3192001-09-22 18:12:08 +0000306 }
307 if( pH->count > pH->htsize ){
308 rehash(pH,pH->htsize*2);
309 }
310 assert( (pH->htsize & (pH->htsize-1))==0 );
311 h = hraw & (pH->htsize-1);
312 elem = pH->ht[h].chain;
313 if( elem ){
314 new_elem->next = elem;
315 new_elem->prev = elem->prev;
316 if( elem->prev ){ elem->prev->next = new_elem; }
317 else { pH->first = new_elem; }
318 elem->prev = new_elem;
319 }else{
320 new_elem->next = pH->first;
321 new_elem->prev = 0;
322 if( pH->first ){ pH->first->prev = new_elem; }
323 pH->first = new_elem;
324 }
325 pH->ht[h].count++;
326 pH->ht[h].chain = new_elem;
327 new_elem->data = data;
328 return 0;
329}