diff options
Diffstat (limited to 'common/dict.c')
-rw-r--r-- | common/dict.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/common/dict.c b/common/dict.c new file mode 100644 index 0000000..2f976c1 --- /dev/null +++ b/common/dict.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2004 Stefan Walter + * Copyright (c) 2011 Collabora Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "config.h" + +#include "dict.h" + +#include <sys/types.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +struct _p11_dict { + p11_dict_hasher hash_func; + p11_dict_equals equal_func; + p11_destroyer key_destroy_func; + p11_destroyer value_destroy_func; + + struct _p11_dictbucket **buckets; + unsigned int num_items; + unsigned int num_buckets; +}; + +typedef struct _p11_dictbucket { + void *key; + unsigned int hashed; + void *value; + struct _p11_dictbucket *next; +} dictbucket; + +static dictbucket * +next_entry (p11_dictiter *iter) +{ + dictbucket *bucket = iter->next; + while (!bucket) { + if (iter->index >= iter->dict->num_buckets) + return NULL; + bucket = iter->dict->buckets[iter->index++]; + } + iter->next = bucket->next; + return bucket; +} + + +int +p11_dict_next (p11_dictiter *iter, + void **key, + void **value) +{ + dictbucket *bucket = next_entry (iter); + if (bucket == NULL) + return 0; + if (key) + *key = bucket->key; + if (value) + *value = bucket->value; + return 1; +} + +void +p11_dict_iterate (p11_dict *dict, + p11_dictiter *iter) +{ + iter->dict = dict; + iter->index = 0; + iter->next = NULL; +} + +static dictbucket ** +lookup_or_create_bucket (p11_dict *dict, + const void *key, + int create) +{ + dictbucket **bucketp; + unsigned int hash; + + /* Perform the hashing */ + hash = dict->hash_func (key); + + /* scan linked list */ + for (bucketp = &dict->buckets[hash % dict->num_buckets]; + *bucketp != NULL; bucketp = &(*bucketp)->next) { + if((*bucketp)->hashed == hash && dict->equal_func ((*bucketp)->key, key)) + break; + } + + if ((*bucketp) != NULL || !create) + return bucketp; + + /* add a new entry for non-NULL val */ + (*bucketp) = calloc (sizeof (dictbucket), 1); + + if (*bucketp != NULL) { + (*bucketp)->key = (void*)key; + (*bucketp)->hashed = hash; + dict->num_items++; + } + + return bucketp; +} + +void * +p11_dict_get (p11_dict *dict, + const void *key) +{ + dictbucket **bucketp; + + bucketp = lookup_or_create_bucket (dict, key, 0); + if (bucketp && *bucketp) + return (void*)((*bucketp)->value); + else + return NULL; +} + +int +p11_dict_set (p11_dict *dict, + void *key, + void *val) +{ + dictbucket **bucketp; + p11_dictiter iter; + dictbucket *bucket; + dictbucket **new_buckets; + unsigned int num_buckets; + + bucketp = lookup_or_create_bucket (dict, key, 1); + if(bucketp && *bucketp) { + + /* Destroy the previous key */ + if ((*bucketp)->key && (*bucketp)->key != key && dict->key_destroy_func) + dict->key_destroy_func ((*bucketp)->key); + + /* Destroy the previous value */ + if ((*bucketp)->value && (*bucketp)->value != val && dict->value_destroy_func) + dict->value_destroy_func ((*bucketp)->value); + + /* replace entry */ + (*bucketp)->key = key; + (*bucketp)->value = val; + + /* check that the collision rate isn't too high */ + if (dict->num_items > dict->num_buckets) { + num_buckets = dict->num_buckets * 2 + 1; + new_buckets = (dictbucket **)calloc (sizeof (dictbucket *), num_buckets); + + /* Ignore failures, maybe we can expand later */ + if(new_buckets) { + p11_dict_iterate (dict, &iter); + while ((bucket = next_entry (&iter)) != NULL) { + unsigned int i = bucket->hashed % num_buckets; + bucket->next = new_buckets[i]; + new_buckets[i] = bucket; + } + + free (dict->buckets); + dict->buckets = new_buckets; + dict->num_buckets = num_buckets; + } + } + + return 1; + } + + return 0; +} + +int +p11_dict_steal (p11_dict *dict, + const void *key, + void **stolen_key, + void **stolen_value) +{ + dictbucket **bucketp; + + bucketp = lookup_or_create_bucket (dict, key, 0); + if (bucketp && *bucketp) { + dictbucket *old = *bucketp; + *bucketp = (*bucketp)->next; + --dict->num_items; + if (stolen_key) + *stolen_key = old->key; + if (stolen_value) + *stolen_value = old->value; + free (old); + return 1; + } + + return 0; + +} + +int +p11_dict_remove (p11_dict *dict, + const void *key) +{ + void *old_key; + void *old_value; + + if (!p11_dict_steal (dict, key, &old_key, &old_value)) + return 0; + + if (dict->key_destroy_func) + dict->key_destroy_func (old_key); + if (dict->value_destroy_func) + dict->value_destroy_func (old_value); + return 1; +} + +void +p11_dict_clear (p11_dict *dict) +{ + dictbucket *bucket, *next; + int i; + + /* Free all entries in the array */ + for (i = 0; i < dict->num_buckets; ++i) { + bucket = dict->buckets[i]; + while (bucket != NULL) { + next = bucket->next; + if (dict->key_destroy_func) + dict->key_destroy_func (bucket->key); + if (dict->value_destroy_func) + dict->value_destroy_func (bucket->value); + free (bucket); + bucket = next; + } + } + + memset (dict->buckets, 0, dict->num_buckets * sizeof (dictbucket *)); + dict->num_items = 0; +} + +p11_dict * +p11_dict_new (p11_dict_hasher hash_func, + p11_dict_equals equal_func, + p11_destroyer key_destroy_func, + p11_destroyer value_destroy_func) +{ + p11_dict *dict; + + assert (hash_func); + assert (equal_func); + + dict = malloc (sizeof (p11_dict)); + if (dict) { + dict->hash_func = hash_func; + dict->equal_func = equal_func; + dict->key_destroy_func = key_destroy_func; + dict->value_destroy_func = value_destroy_func; + + dict->num_buckets = 9; + dict->buckets = (dictbucket **)calloc (sizeof (dictbucket *), dict->num_buckets); + if (!dict->buckets) { + free (dict); + return NULL; + } + + dict->num_items = 0; + } + + return dict; +} + +void +p11_dict_free (p11_dict *dict) +{ + dictbucket *bucket; + p11_dictiter iter; + + if (!dict) + return; + + p11_dict_iterate (dict, &iter); + while ((bucket = next_entry (&iter)) != NULL) { + if (dict->key_destroy_func) + dict->key_destroy_func (bucket->key); + if (dict->value_destroy_func) + dict->value_destroy_func (bucket->value); + free (bucket); + } + + if (dict->buckets) + free (dict->buckets); + + free (dict); +} + +unsigned int +p11_dict_size (p11_dict *dict) +{ + return dict->num_items; +} + +unsigned int +p11_dict_str_hash (const void *string) +{ + const char *p = string; + unsigned int hash = *p; + + if (hash) + for (p += 1; *p != '\0'; p++) + hash = (hash << 5) - hash + *p; + + return hash; +} + +int +p11_dict_str_equal (const void *string_one, + const void *string_two) +{ + assert (string_one); + assert (string_two); + + return strcmp (string_one, string_two) == 0; +} + +unsigned int +p11_dict_ulongptr_hash (const void *to_ulong) +{ + assert (to_ulong); + return (unsigned int)*((unsigned long*)to_ulong); +} + +int +p11_dict_ulongptr_equal (const void *ulong_one, + const void *ulong_two) +{ + assert (ulong_one); + assert (ulong_two); + return *((unsigned long*)ulong_one) == *((unsigned long*)ulong_two); +} + +unsigned int +p11_dict_intptr_hash (const void *to_int) +{ + assert (to_int); + return (unsigned int)*((int*)to_int); +} + +int +p11_dict_intptr_equal (const void *int_one, + const void *int_two) +{ + assert (int_one); + assert (int_two); + return *((int*)int_one) == *((int*)int_two); +} + +unsigned int +p11_dict_direct_hash (const void *ptr) +{ + return (unsigned int)(size_t)ptr; +} + +int +p11_dict_direct_equal (const void *ptr_one, + const void *ptr_two) +{ + return ptr_one == ptr_two; +} |