Newer
Older
/***************************************************************************
Daniel Stenberg
committed
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
Daniel Stenberg
committed
* Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
Daniel Stenberg
committed
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#include <string.h>
#include "hash.h"
#include "llist.h"
#include "memory.h"
/* this must be the last include file */
#include "memdebug.h"
Daniel Stenberg
committed
static void
hash_element_dtor(void *user, void *element)
Daniel Stenberg
committed
struct curl_hash *h = (struct curl_hash *) user;
struct curl_hash_element *e = (struct curl_hash_element *) element;
Daniel Stenberg
committed
if (e->key)
free(e->key);
h->dtor(e->ptr);
free(e);
}
Daniel Stenberg
committed
Curl_hash_init(struct curl_hash *h,
int slots,
hash_function hfunc,
comp_function comparator,
curl_hash_dtor dtor)
{
int i;
Daniel Stenberg
committed
if (!slots || !hfunc || !comparator ||!dtor) {
return 1; /* failure */
}
h->hash_func = hfunc;
h->comp_func = comparator;
h->dtor = dtor;
h->size = 0;
Daniel Stenberg
committed
h->slots = slots;
Daniel Stenberg
committed
h->table = (struct curl_llist **) malloc(slots * sizeof(struct curl_llist *));
h->table[i] = Curl_llist_alloc((curl_llist_dtor) hash_element_dtor);
if(!h->table[i]) {
while(i--)
Curl_llist_destroy(h->table[i], NULL);
free(h->table);
return 1; /* failure */
}
}
return 0; /* fine */
Daniel Stenberg
committed
struct curl_hash *
Daniel Stenberg
committed
Curl_hash_alloc(int slots,
hash_function hfunc,
comp_function comparator,
curl_hash_dtor dtor)
Daniel Stenberg
committed
struct curl_hash *h;
Daniel Stenberg
committed
if (!slots || !hfunc || !comparator ||!dtor) {
return NULL; /* failure */
}
Daniel Stenberg
committed
h = (struct curl_hash *) malloc(sizeof(struct curl_hash));
Daniel Stenberg
committed
if(Curl_hash_init(h, slots, hfunc, comparator, dtor)) {
return h;
}
Daniel Stenberg
committed
static struct curl_hash_element *
Daniel Stenberg
committed
mk_hash_element(void *key, size_t key_len, const void *p)
{
Daniel Stenberg
committed
struct curl_hash_element *he =
(struct curl_hash_element *) malloc(sizeof(struct curl_hash_element));
if(he) {
Daniel Stenberg
committed
void *dup = malloc(key_len);
Daniel Stenberg
committed
/* copy the key */
memcpy(dup, key, key_len);
he->key = dup;
he->key_len = key_len;
he->ptr = (void *) p;
}
else {
/* failed to duplicate the key, free memory and fail */
free(he);
he = NULL;
}
}
return he;
Daniel Stenberg
committed
#define FETCH_LIST(x,y,z) x->table[x->hash_func(y, z, x->slots)]
Daniel Stenberg
committed
/* Return the data in the hash. If there already was a match in the hash,
that data is returned. */
void *
Daniel Stenberg
committed
Curl_hash_add(struct curl_hash *h, void *key, size_t key_len, void *p)
Daniel Stenberg
committed
struct curl_hash_element *he;
struct curl_llist_element *le;
Daniel Stenberg
committed
struct curl_llist *l = FETCH_LIST (h, key, key_len);
Daniel Stenberg
committed
for (le = l->head; le; le = le->next) {
Daniel Stenberg
committed
he = (struct curl_hash_element *) le->ptr;
Daniel Stenberg
committed
if (h->comp_func(he->key, he->key_len, key, key_len)) {
Daniel Stenberg
committed
h->dtor(p); /* remove the NEW entry */
return he->ptr; /* return the EXISTING entry */
he = mk_hash_element(key, key_len, p);
if (he) {
if(Curl_llist_insert_next(l, l->tail, he)) {
++h->size;
return p; /* return the new entry */
}
Daniel Stenberg
committed
/*
* Couldn't insert it, destroy the 'he' element and the key again. We
* don't call hash_element_dtor() since that would also call the
* "destructor" for the actual data 'p'. When we fail, we shall not touch
* that data.
*/
free(he->key);
free(he);
Daniel Stenberg
committed
return NULL; /* failure */
Daniel Stenberg
committed
/* remove the identified hash entry, returns non-zero on failure */
Daniel Stenberg
committed
int Curl_hash_delete(struct curl_hash *h, void *key, size_t key_len)
Daniel Stenberg
committed
{
struct curl_llist_element *le;
struct curl_hash_element *he;
struct curl_llist *l = FETCH_LIST(h, key, key_len);
for (le = l->head; le; le = le->next) {
he = le->ptr;
Daniel Stenberg
committed
if (h->comp_func(he->key, he->key_len, key, key_len)) {
Daniel Stenberg
committed
Curl_llist_remove(l, le, (void *) h);
return 0;
}
}
return 1;
}
void *
Daniel Stenberg
committed
Curl_hash_pick(struct curl_hash *h, void *key, size_t key_len)
Daniel Stenberg
committed
struct curl_llist_element *le;
struct curl_hash_element *he;
struct curl_llist *l = FETCH_LIST(h, key, key_len);
Daniel Stenberg
committed
for (le = l->head; le; le = le->next) {
he = le->ptr;
Daniel Stenberg
committed
if (h->comp_func(he->key, he->key_len, key, key_len)) {
return he->ptr;
return NULL;
#if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
Daniel Stenberg
committed
void
Curl_hash_apply(curl_hash *h, void *user,
void (*cb)(void *user, void *ptr))
Daniel Stenberg
committed
struct curl_llist_element *le;
int i;
for (i = 0; i < h->slots; ++i) {
for (le = (h->table[i])->head;
le;
le = le->next) {
curl_hash_element *el = le->ptr;
cb(user, el->ptr);
}
}
}
#endif
Daniel Stenberg
committed
Curl_hash_clean(struct curl_hash *h)
{
int i;
for (i = 0; i < h->slots; ++i) {
Curl_llist_destroy(h->table[i], (void *) h);
}
free(h->table);
}
Daniel Stenberg
committed
Curl_hash_clean_with_criterium(struct curl_hash *h, void *user,
int (*comp)(void *, void *))
Daniel Stenberg
committed
struct curl_llist_element *le;
struct curl_llist_element *lnext;
struct curl_llist *list;
int i;
for (i = 0; i < h->slots; ++i) {
list = h->table[i];
le = list->head; /* get first list entry */
while(le) {
Daniel Stenberg
committed
struct curl_hash_element *he = le->ptr;
lnext = le->next;
/* ask the callback function if we shall remove this entry or not */
if (comp(user, he->ptr)) {
Curl_llist_remove(list, le, (void *) h);
--h->size; /* one less entry in the hash now */
le = lnext;
}
Daniel Stenberg
committed
void
Daniel Stenberg
committed
Curl_hash_destroy(struct curl_hash *h)
if (!h)
return;
Curl_hash_clean(h);
free(h);
}
Daniel Stenberg
committed
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
size_t Curl_hash_str(void* key, size_t key_length, size_t slots_num)
{
char* key_str = (char *) key;
char *end = (char *) key_str + key_length;
unsigned long h = 5381;
while (key_str < end) {
h += h << 5;
h ^= (unsigned long) *key_str++;
}
return (h % slots_num);
}
size_t Curl_str_key_compare(void*k1, size_t key1_len, void*k2, size_t key2_len)
{
char *key1 = (char *)k1;
char *key2 = (char *)k2;
if (key1_len == key2_len &&
*key1 == *key2 &&
memcmp(key1, key2, key1_len) == 0) {
return 1;
}
return 0;
}
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#if 0 /* useful function for debugging hashes and their contents */
void Curl_hash_print(struct curl_hash *h,
void (*func)(void *))
{
int i;
struct curl_llist_element *le;
struct curl_llist *list;
struct curl_hash_element *he;
if (!h)
return;
fprintf(stderr, "=Hash dump=\n");
for (i = 0; i < h->slots; i++) {
list = h->table[i];
le = list->head; /* get first list entry */
if(le) {
fprintf(stderr, "index %d:", i);
while(le) {
he = le->ptr;
if(func)
func(he->ptr);
else
fprintf(stderr, " [%p]", he->ptr);
le = le->next;
}
fprintf(stderr, "\n");
}
}
}
#endif