author | Mikael Berthe <mikael@lilotux.net> |
Mon, 15 May 2006 23:06:13 +0200 | |
changeset 857 | ef35a2bb40d7 |
parent 417 | c3ae9251c197 |
permissions | -rw-r--r-- |
25 | 1 |
/* |
2 |
* This program is free software; you can redistribute it and/or modify |
|
3 |
* it under the terms of the GNU General Public License as published by |
|
4 |
* the Free Software Foundation; either version 2 of the License, or |
|
5 |
* (at your option) any later version. |
|
6 |
* |
|
7 |
* This program is distributed in the hope that it will be useful, |
|
8 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
9 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
10 |
* GNU General Public License for more details. |
|
11 |
* |
|
12 |
* You should have received a copy of the GNU General Public License |
|
13 |
* along with this program; if not, write to the Free Software |
|
14 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
15 |
* |
|
417
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
16 |
* Copyrights |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
17 |
* |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
18 |
* Portions created by or assigned to Jabber.com, Inc. are |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
19 |
* Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
20 |
* information for Jabber.com, Inc. is available at http://www.jabber.com/. |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
21 |
* |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
22 |
* Portions Copyright (c) 1998-1999 Jeremie Miller. |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
23 |
* |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
24 |
* Acknowledgements |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
25 |
* |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
26 |
* Special thanks to the Jabber Open Source Contributors for their |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
27 |
* suggestions and support of Jabber. |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
28 |
* |
25 | 29 |
*/ |
30 |
#include <libxode.h> |
|
31 |
||
32 |
/***************************************************************************** |
|
33 |
* Internal type definitions |
|
34 |
*/ |
|
35 |
||
36 |
typedef struct tagHNODE |
|
37 |
{ |
|
38 |
struct tagHNODE *next; /* next node in list */ |
|
39 |
const void *key; /* key pointer */ |
|
40 |
void *value; /* value pointer */ |
|
41 |
} HNODE; |
|
42 |
||
43 |
#define SLAB_NUM_NODES 64 /* allocate this many nodes per slab */ |
|
44 |
||
45 |
typedef struct tagHSLAB |
|
46 |
{ |
|
47 |
struct tagHSLAB *next; /* next slab pointer */ |
|
48 |
HNODE nodes[SLAB_NUM_NODES]; /* the actual nodes */ |
|
49 |
} HSLAB; |
|
50 |
||
51 |
#define HASH_NUM_BUCKETS 509 /* should be a prime number; see Knuth */ |
|
52 |
||
53 |
typedef struct tagHASHTABLE_INTERNAL |
|
54 |
{ |
|
55 |
unsigned long sig1; /* first signature word */ |
|
56 |
KEYHASHFUNC hash; /* hash function */ |
|
57 |
KEYCOMPAREFUNC cmp; /* comparison function */ |
|
58 |
int count; /* table entry count */ |
|
59 |
int bcount; /* bucket count */ |
|
60 |
HNODE **buckets; /* the hash buckets */ |
|
61 |
unsigned long sig2; /* second signature word */ |
|
62 |
||
63 |
} HASHTABLE_INTERNAL; |
|
64 |
||
65 |
#define HASH_SIG1 0x68736148UL /* "Hash" */ |
|
66 |
#define HASH_SIG2 0x6F627245UL /* "Erbo" */ |
|
67 |
||
68 |
#define do_hash(tb,key) ((*((tb)->hash))(key) % ((tb)->bcount)) |
|
69 |
||
70 |
static HNODE *s_free_nodes = NULL; /* free nodes list */ |
|
71 |
static HSLAB *s_slabs = NULL; /* node slabs list */ |
|
72 |
||
73 |
/***************************************************************************** |
|
74 |
* Internal functions |
|
75 |
*/ |
|
76 |
||
77 |
static HNODE *allocate_node( |
|
78 |
const void *key, /* key pointer for this node */ |
|
79 |
void *value) /* value pointer for this node */ |
|
80 |
/* |
|
81 |
allocate_node allocates a new hash node and fills it. Returns NULL if the |
|
82 |
node could not be allocated. |
|
83 |
*/ |
|
84 |
{ |
|
85 |
HNODE *rc; /* return from this function */ |
|
86 |
||
87 |
if (!s_free_nodes) |
|
88 |
{ /* allocate a new slabful of nodes and chain them to make a new free list */ |
|
89 |
register int i; /* loop counter */ |
|
90 |
HSLAB *slab = (HSLAB *)malloc(sizeof(HSLAB)); |
|
91 |
if (!slab) |
|
92 |
return NULL; |
|
93 |
memset(slab,0,sizeof(HSLAB)); |
|
94 |
slab->next = s_slabs; |
|
95 |
for (i=0; i<(SLAB_NUM_NODES-1); i++) |
|
96 |
slab->nodes[i].next = &(slab->nodes[i+1]); |
|
97 |
s_free_nodes = &(slab->nodes[0]); |
|
98 |
s_slabs = slab; |
|
99 |
||
100 |
} /* end if */ |
|
101 |
||
102 |
/* grab a node off the fron of the free list and fill it */ |
|
103 |
rc = s_free_nodes; |
|
104 |
s_free_nodes = rc->next; |
|
105 |
rc->next = NULL; |
|
106 |
rc->key = key; |
|
107 |
rc->value = value; |
|
108 |
return rc; |
|
109 |
||
110 |
} /* end allocate_node */ |
|
111 |
||
112 |
static void free_node( |
|
113 |
HNODE *node) /* node to be freed */ |
|
114 |
/* |
|
115 |
free_node returns a hash node to the list. |
|
116 |
*/ |
|
117 |
{ |
|
118 |
/* zap the node contents to avoid problems later */ |
|
119 |
memset(node,0,sizeof(HNODE)); |
|
120 |
||
121 |
/* chain it onto the free list */ |
|
122 |
node->next = s_free_nodes; |
|
123 |
s_free_nodes = node; |
|
124 |
||
125 |
} /* end free_node */ |
|
126 |
||
127 |
static HNODE *find_node( |
|
128 |
HASHTABLE_INTERNAL *tab, /* pointer to hash table */ |
|
129 |
const void *key, /* key value to look up */ |
|
130 |
int bucket) /* bucket number (-1 to have function compute it) */ |
|
131 |
/* |
|
132 |
find_node walks a hash bucket to find a node whose key matches the named key value. |
|
133 |
Returns the node pointer, or NULL if it's not found. |
|
134 |
*/ |
|
135 |
{ |
|
136 |
register HNODE *p; /* search pointer/return from this function */ |
|
137 |
||
138 |
if (bucket<0) /* compute hash value if we don't know it already */ |
|
139 |
bucket = do_hash(tab,key); |
|
140 |
||
141 |
/* search through the bucket contents */ |
|
142 |
for (p=tab->buckets[bucket]; p; p=p->next) |
|
143 |
if ((*(tab->cmp))(key,p->key)==0) |
|
144 |
return p; /* found! */ |
|
145 |
||
146 |
return NULL; /* not found */ |
|
147 |
||
148 |
} /* end find_node */ |
|
149 |
||
150 |
static HASHTABLE_INTERNAL *handle2ptr( |
|
151 |
HASHTABLE tbl) /* hash table handle */ |
|
152 |
/* |
|
153 |
handle2ptr converts a hash table handle into a pointer and checks its signatures |
|
154 |
to make sure someone's not trying to pull a whizzer on this module. |
|
155 |
*/ |
|
156 |
{ |
|
157 |
register HASHTABLE_INTERNAL *rc = (HASHTABLE_INTERNAL *)tbl; |
|
158 |
if ((rc->sig1==HASH_SIG1) && (rc->sig2==HASH_SIG2)) |
|
159 |
return rc; /* signatures match */ |
|
160 |
else |
|
161 |
return NULL; /* yIkes! */ |
|
162 |
} |
|
163 |
||
164 |
/***************************************************************************** |
|
165 |
* External functions |
|
166 |
*/ |
|
167 |
||
168 |
HASHTABLE ghash_create(int buckets, KEYHASHFUNC hash, KEYCOMPAREFUNC cmp) |
|
169 |
/* |
|
170 |
Description: |
|
171 |
Creates a new hash table. |
|
172 |
||
173 |
Input: |
|
174 |
Parameters: |
|
175 |
buckets - Number of buckets to allocate for the hash table; this value |
|
176 |
should be a prime number for maximum efficiency. |
|
177 |
hash - Key hash code function to use. |
|
178 |
cmp - Key comparison function to use. |
|
179 |
||
180 |
Output: |
|
181 |
Returns: |
|
182 |
NULL - Table could not be allocated. |
|
183 |
Other - Handle to the new hashtable. |
|
184 |
*/ |
|
185 |
{ |
|
186 |
HASHTABLE_INTERNAL *tab; /* new table structure */ |
|
187 |
char *allocated; |
|
188 |
||
189 |
if (!hash || !cmp) |
|
190 |
return NULL; /* bogus! */ |
|
191 |
||
192 |
if (buckets<=0) |
|
193 |
buckets = HASH_NUM_BUCKETS; |
|
194 |
||
195 |
/* allocate a hash table structure */ |
|
196 |
allocated = malloc(sizeof(HASHTABLE_INTERNAL) + (buckets * sizeof(HNODE *))); |
|
197 |
if (!allocated) |
|
198 |
return NULL; /* memory error */ |
|
199 |
||
200 |
/* fill the fields of the hash table */ |
|
201 |
tab = (HASHTABLE_INTERNAL *)allocated; |
|
202 |
allocated += sizeof(HASHTABLE_INTERNAL); |
|
203 |
memset(tab,0,sizeof(HASHTABLE_INTERNAL)); |
|
204 |
memset(allocated,0,buckets * sizeof(HNODE *)); |
|
205 |
tab->sig1 = HASH_SIG1; |
|
206 |
tab->hash = hash; |
|
207 |
tab->cmp = cmp; |
|
208 |
tab->bcount = buckets; |
|
209 |
tab->buckets = (HNODE **)allocated; |
|
210 |
tab->sig2 = HASH_SIG2; |
|
211 |
||
212 |
return (HASHTABLE)tab; /* Qa'pla! */ |
|
213 |
||
214 |
} /* end ghash_create */ |
|
215 |
||
216 |
void ghash_destroy(HASHTABLE tbl) |
|
217 |
/* |
|
218 |
Description: |
|
219 |
Destroys a hash table. |
|
220 |
||
221 |
Input: |
|
222 |
Parameters: |
|
223 |
tbl - Table to be destroyed. |
|
224 |
||
225 |
Output: |
|
226 |
Returns: |
|
227 |
Nothing. |
|
228 |
*/ |
|
229 |
{ |
|
230 |
HASHTABLE_INTERNAL *tab; /* new table structure */ |
|
231 |
int i; /* loop counter */ |
|
232 |
HNODE *p, *p2; /* temporary pointers */ |
|
233 |
||
234 |
if (!tbl) |
|
235 |
return; /* bogus! */ |
|
236 |
||
237 |
/* Convert the handle to a table pointer. */ |
|
238 |
tab = handle2ptr(tbl); |
|
239 |
if (!tab) |
|
240 |
return; |
|
241 |
||
242 |
/* Nuke the nodes it contains. */ |
|
243 |
for (i=0; i<tab->bcount; i++) |
|
244 |
{ /* free the contents of each bucket */ |
|
245 |
p = tab->buckets[i]; |
|
246 |
while (p) |
|
247 |
{ /* free each node in turn */ |
|
248 |
p2 = p->next; |
|
249 |
free_node(p); |
|
250 |
p = p2; |
|
251 |
||
252 |
} /* end while */ |
|
253 |
||
254 |
} /* end for */ |
|
255 |
||
256 |
free(tab); /* bye bye now! */ |
|
257 |
||
258 |
} /* end ghash_destroy */ |
|
259 |
||
260 |
void *ghash_get(HASHTABLE tbl, const void *key) |
|
261 |
/* |
|
262 |
Description: |
|
263 |
Retrieves a value stored in the hash table. |
|
264 |
||
265 |
Input: |
|
266 |
Parameters: |
|
267 |
tbl - The hash table to look in. |
|
268 |
key - The key value to search on. |
|
269 |
||
270 |
Output: |
|
271 |
Returns: |
|
272 |
NULL - Value not found. |
|
273 |
Other - Value corresponding to the specified key. |
|
274 |
*/ |
|
275 |
{ |
|
276 |
HASHTABLE_INTERNAL *tab; /* internal table pointer */ |
|
277 |
HNODE *node; /* hash node */ |
|
278 |
void *rc = NULL; /* return from this function */ |
|
279 |
||
280 |
if (!tbl || !key) |
|
281 |
return NULL; /* bogus! */ |
|
282 |
||
283 |
/* Convert the handle to a table pointer. */ |
|
284 |
tab = handle2ptr(tbl); |
|
285 |
if (!tab) |
|
286 |
return NULL; /* error */ |
|
287 |
||
288 |
/* Attempt to find the node. */ |
|
289 |
node = find_node(tab,key,-1); |
|
290 |
if (node) |
|
291 |
rc = node->value; /* found it! */ |
|
292 |
||
293 |
return rc; |
|
294 |
||
295 |
} /* end ghash_get */ |
|
296 |
||
297 |
int ghash_put(HASHTABLE tbl, const void *key, void *value) |
|
298 |
/* |
|
299 |
Description: |
|
300 |
Associates a key with a value in this hash table. |
|
301 |
||
302 |
Input: |
|
303 |
Parameters: |
|
304 |
tbl - Hash table to add. |
|
305 |
key - Key to use for the value in the table. |
|
306 |
value - Value to add for this key. |
|
307 |
||
308 |
Output: |
|
309 |
Returns: |
|
310 |
1 - Success. |
|
311 |
0 - Failure. |
|
312 |
||
313 |
Notes: |
|
314 |
If the specified key is already in the hashtable, its value will be replaced. |
|
315 |
*/ |
|
316 |
{ |
|
317 |
HASHTABLE_INTERNAL *tab; /* internal table pointer */ |
|
318 |
int bucket; /* bucket value goes into */ |
|
319 |
HNODE *node; /* hash node */ |
|
320 |
int rc = 1; /* return from this function */ |
|
321 |
||
322 |
if (!tbl || !key || !value) |
|
323 |
return 0; /* bogus! */ |
|
324 |
||
325 |
/* Convert the handle to a table pointer. */ |
|
326 |
tab = handle2ptr(tbl); |
|
327 |
if (!tab) |
|
328 |
return 0; /* error */ |
|
329 |
||
330 |
||
331 |
/* Compute the hash bucket and try to find an existing node. */ |
|
332 |
bucket = do_hash(tab,key); |
|
333 |
node = find_node(tab,key,bucket); |
|
334 |
if (!node) |
|
335 |
{ /* OK, try to allocate a new node. */ |
|
336 |
node = allocate_node(key,value); |
|
337 |
if (node) |
|
338 |
{ /* Chain the new node into the hash table. */ |
|
339 |
node->next = tab->buckets[bucket]; |
|
340 |
tab->buckets[bucket] = node; |
|
341 |
tab->count++; |
|
342 |
||
343 |
} /* end if */ |
|
344 |
else /* allocation error */ |
|
345 |
rc = 0; |
|
346 |
||
347 |
} /* end if */ |
|
348 |
else /* already in table - just reassign value */ |
|
349 |
node->value = value; |
|
350 |
||
351 |
return rc; |
|
352 |
||
353 |
} /* end ghash_put */ |
|
354 |
||
355 |
int ghash_remove(HASHTABLE tbl, const void *key) |
|
356 |
/* |
|
357 |
Description: |
|
358 |
Removes an entry from a hash table, given its key. |
|
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
359 |
|
25 | 360 |
Input: |
361 |
Parameters: |
|
362 |
tbl - Hash table to remove from. |
|
363 |
key - Key of value to remove. |
|
364 |
||
365 |
Output: |
|
366 |
Returns: |
|
367 |
1 - Success. |
|
368 |
0 - Failure; key not present in hash table. |
|
369 |
*/ |
|
370 |
{ |
|
371 |
HASHTABLE_INTERNAL *tab; /* internal table pointer */ |
|
372 |
int bucket; /* bucket value goes into */ |
|
373 |
HNODE *node; /* hash node */ |
|
374 |
register HNODE *p; /* removal pointer */ |
|
375 |
int rc = 1; /* return from this function */ |
|
376 |
||
377 |
if (!tbl || !key) |
|
378 |
return 0; /* bogus! */ |
|
379 |
||
380 |
/* Convert the handle to a table pointer. */ |
|
381 |
tab = handle2ptr(tbl); |
|
382 |
if (!tab) |
|
383 |
return 0; /* error */ |
|
384 |
||
385 |
||
386 |
/* Compute the hash bucket and try to find an existing node. */ |
|
387 |
bucket = do_hash(tab,key); |
|
388 |
node = find_node(tab,key,bucket); |
|
389 |
if (node) |
|
390 |
{ /* look to unchain it from the bucket it's in */ |
|
391 |
if (node==tab->buckets[bucket]) |
|
392 |
tab->buckets[bucket] = node->next; /* unchain at head */ |
|
393 |
else |
|
394 |
{ /* unchain in middle of list */ |
|
395 |
for (p=tab->buckets[bucket]; p->next!=node; p=p->next) ; |
|
396 |
p->next = node->next; |
|
397 |
||
398 |
} /* end else */ |
|
399 |
||
400 |
free_node(node); /* bye bye now! */ |
|
401 |
tab->count--; |
|
402 |
||
403 |
} /* end if */ |
|
404 |
else /* node not found */ |
|
405 |
rc = 0; |
|
406 |
||
407 |
return rc; |
|
408 |
||
409 |
} /* end ghash_remove */ |
|
410 |
||
411 |
int ghash_walk(HASHTABLE tbl, TABLEWALKFUNC func, void *user_data) |
|
412 |
/* |
|
413 |
Description: |
|
414 |
"Walks" through a hash table, calling a callback function for each element |
|
415 |
stored in it. |
|
416 |
||
417 |
Input: |
|
418 |
Parameters: |
|
419 |
tbl - Hash table to walk. |
|
420 |
func - Function to be called for each node. It takes three parameters, |
|
421 |
a user data pointer, a key value pointer, and a data value pointer. |
|
422 |
It returns 0 to stop the enumeration or 1 to keep it going. |
|
423 |
user_data - Value to use as the first parameter for the callback |
|
424 |
function. |
|
425 |
||
426 |
Output: |
|
427 |
Returns: |
|
428 |
0 - Error occurred. |
|
429 |
Other - Number of nodes visited up to and including the one for which |
|
430 |
the callback function returned 0, if it did; ranges from 1 |
|
431 |
to the number of nodes in the hashtable. |
|
432 |
*/ |
|
433 |
{ |
|
434 |
HASHTABLE_INTERNAL *tab; /* internal table pointer */ |
|
435 |
int i; /* loop counter */ |
|
436 |
int running = 1; /* we're still running */ |
|
437 |
int count = 0; /* number of nodes visited before stop node */ |
|
438 |
register HNODE *p, *p2; /* loop pointer */ |
|
439 |
||
440 |
if (!tbl || !func) |
|
441 |
return -1; /* bogus values! */ |
|
442 |
||
443 |
/* Convert the handle to a table pointer. */ |
|
444 |
tab = handle2ptr(tbl); |
|
445 |
if (!tab) |
|
446 |
return -1; /* error */ |
|
447 |
||
448 |
||
449 |
for (i=0; running && (i<tab->bcount); i++) |
|
450 |
{ /* visit the contents of each bucket */ |
|
451 |
p = tab->buckets[i]; |
|
452 |
while (running && p) |
|
453 |
{ /* visit each node in turn */ |
|
454 |
p2 = p->next; |
|
455 |
count++; |
|
456 |
running = (*func)(user_data,p->key,p->value); |
|
457 |
p = p2; |
|
458 |
||
459 |
} /* end while */ |
|
460 |
||
461 |
} /* end for */ |
|
462 |
||
463 |
return count; |
|
464 |
||
465 |
} /* end ghash_walk */ |
|
466 |
||
467 |
int str_hash_code(const char *s) |
|
468 |
/* |
|
469 |
Description: |
|
470 |
Generates a hash code for a string. This function uses the ELF hashing |
|
471 |
algorithm as reprinted in Andrew Binstock, "Hashing Rehashed," _Dr. |
|
472 |
Dobb's Journal_, April 1996. |
|
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
473 |
|
25 | 474 |
Input: |
475 |
Parameters: |
|
476 |
s - The string to be hashed. |
|
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
477 |
|
25 | 478 |
Output: |
479 |
Returns: |
|
480 |
A hash code for the string. |
|
481 |
*/ |
|
482 |
{ |
|
483 |
/* ELF hash uses unsigned chars and unsigned arithmetic for portability */ |
|
484 |
const unsigned char *name = (const unsigned char *)s; |
|
485 |
unsigned long h = 0, g; |
|
486 |
||
487 |
if (!name) |
|
488 |
return 0; /* anti-NULL guard not in the original */ |
|
489 |
||
490 |
while (*name) |
|
491 |
{ /* do some fancy bitwanking on the string */ |
|
492 |
h = (h << 4) + (unsigned long)(*name++); |
|
493 |
if ((g = (h & 0xF0000000UL))!=0) |
|
494 |
h ^= (g >> 24); |
|
495 |
h &= ~g; |
|
496 |
||
497 |
} /* end while */ |
|
498 |
||
499 |
return (int)h; |
|
500 |
||
501 |
} |