1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: hash.c,v 1.2 2007-03-15 19:22:13 andy Exp $
22 ***************************************************************************/
33 /* this must be the last include file */
37 hash_str(const char *key
, size_t key_length
)
39 char *end
= (char *) key
+ key_length
;
40 unsigned long h
= 5381;
44 h
^= (unsigned long) *key
++;
51 hash_element_dtor(void *user
, void *element
)
53 struct curl_hash
*h
= (struct curl_hash
*) user
;
54 struct curl_hash_element
*e
= (struct curl_hash_element
*) element
;
64 /* return 1 on error, 0 is fine */
66 Curl_hash_init(struct curl_hash
*h
, int slots
, curl_hash_dtor dtor
)
74 h
->table
= (struct curl_llist
**) malloc(slots
* sizeof(struct curl_llist
*));
76 for (i
= 0; i
< slots
; ++i
) {
77 h
->table
[i
] = Curl_llist_alloc((curl_llist_dtor
) hash_element_dtor
);
80 Curl_llist_destroy(h
->table
[i
], NULL
);
82 return 1; /* failure */
88 return 1; /* failure */
92 Curl_hash_alloc(int slots
, curl_hash_dtor dtor
)
96 h
= (struct curl_hash
*) malloc(sizeof(struct curl_hash
));
98 if(Curl_hash_init(h
, slots
, dtor
)) {
109 hash_key_compare(char *key1
, size_t key1_len
, char *key2
, size_t key2_len
)
111 if (key1_len
== key2_len
&&
113 memcmp(key1
, key2
, key1_len
) == 0) {
120 static struct curl_hash_element
*
121 mk_hash_element(char *key
, size_t key_len
, const void *p
)
123 struct curl_hash_element
*he
=
124 (struct curl_hash_element
*) malloc(sizeof(struct curl_hash_element
));
127 char *dup
= malloc(key_len
);
130 memcpy(dup
, key
, key_len
);
133 he
->key_len
= key_len
;
134 he
->ptr
= (void *) p
;
137 /* failed to duplicate the key, free memory and fail */
145 #define find_slot(__h, __k, __k_len) (hash_str(__k, __k_len) % (__h)->slots)
147 #define FETCH_LIST(x,y,z) x->table[find_slot(x, y, z)]
149 /* Return the data in the hash. If there already was a match in the hash,
150 that data is returned. */
152 Curl_hash_add(struct curl_hash
*h
, char *key
, size_t key_len
, void *p
)
154 struct curl_hash_element
*he
;
155 struct curl_llist_element
*le
;
156 struct curl_llist
*l
= FETCH_LIST(h
, key
, key_len
);
158 for (le
= l
->head
; le
; le
= le
->next
) {
159 he
= (struct curl_hash_element
*) le
->ptr
;
160 if (hash_key_compare(he
->key
, he
->key_len
, key
, key_len
)) {
161 h
->dtor(p
); /* remove the NEW entry */
162 return he
->ptr
; /* return the EXISTING entry */
166 he
= mk_hash_element(key
, key_len
, p
);
168 if(Curl_llist_insert_next(l
, l
->tail
, he
)) {
170 return p
; /* return the new entry */
173 * Couldn't insert it, destroy the 'he' element and the key again. We
174 * don't call hash_element_dtor() since that would also call the
175 * "destructor" for the actual data 'p'. When we fail, we shall not touch
182 return NULL
; /* failure */
185 /* remove the identified hash entry, returns non-zero on failure */
186 int Curl_hash_delete(struct curl_hash
*h
, char *key
, size_t key_len
)
188 struct curl_llist_element
*le
;
189 struct curl_hash_element
*he
;
190 struct curl_llist
*l
= FETCH_LIST(h
, key
, key_len
);
192 for (le
= l
->head
; le
; le
= le
->next
) {
194 if (hash_key_compare(he
->key
, he
->key_len
, key
, key_len
)) {
195 Curl_llist_remove(l
, le
, (void *) h
);
203 Curl_hash_pick(struct curl_hash
*h
, char *key
, size_t key_len
)
205 struct curl_llist_element
*le
;
206 struct curl_hash_element
*he
;
207 struct curl_llist
*l
= FETCH_LIST(h
, key
, key_len
);
209 for (le
= l
->head
; le
; le
= le
->next
) {
211 if (hash_key_compare(he
->key
, he
->key_len
, key
, key_len
)) {
219 #if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
221 Curl_hash_apply(curl_hash
*h
, void *user
,
222 void (*cb
)(void *user
, void *ptr
))
224 struct curl_llist_element
*le
;
227 for (i
= 0; i
< h
->slots
; ++i
) {
228 for (le
= (h
->table
[i
])->head
;
231 curl_hash_element
*el
= le
->ptr
;
239 Curl_hash_clean(struct curl_hash
*h
)
243 for (i
= 0; i
< h
->slots
; ++i
) {
244 Curl_llist_destroy(h
->table
[i
], (void *) h
);
251 Curl_hash_clean_with_criterium(struct curl_hash
*h
, void *user
,
252 int (*comp
)(void *, void *))
254 struct curl_llist_element
*le
;
255 struct curl_llist_element
*lnext
;
256 struct curl_llist
*list
;
259 for (i
= 0; i
< h
->slots
; ++i
) {
261 le
= list
->head
; /* get first list entry */
263 struct curl_hash_element
*he
= le
->ptr
;
265 /* ask the callback function if we shall remove this entry or not */
266 if (comp(user
, he
->ptr
)) {
267 Curl_llist_remove(list
, le
, (void *) h
);
268 --h
->size
; /* one less entry in the hash now */
276 Curl_hash_destroy(struct curl_hash
*h
)
285 #if 0 /* useful function for debugging hashes and their contents */
286 void Curl_hash_print(struct curl_hash
*h
,
287 void (*func
)(void *))
290 struct curl_llist_element
*le
;
291 struct curl_llist
*list
;
292 struct curl_hash_element
*he
;
296 fprintf(stderr
, "=Hash dump=\n");
298 for (i
= 0; i
< h
->slots
; i
++) {
300 le
= list
->head
; /* get first list entry */
302 fprintf(stderr
, "index %d:", i
);
308 fprintf(stderr
, " [%p]", he
->ptr
);
311 fprintf(stderr
, "\n");