Re: [PATCH] generic lookup dictionary (hash) - extends gwlib/dict.[ch]

Stipe Tolj <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Organization tolj.org system architecture
Message-ID <[email protected]>
Alexander Malysh wrote:

> Hi,
> 
> I don't know now how to use this code but code reviews show:

usage example is in test/test_gw_dict.c, which is attached in the patch.

> 1) gw_dict_ops_t doesn't define function to destroy key. Because key is  
> not Octstr anymore you need to know which function to call.
> 
> 2) because of (1) you don't destroy keys => memleak

yep, now we have an extended struct:

struct gw_dict_ops {
     void (*destroy) (void *item);
     void *(*key_duplicate) (void *key);
     void (*key_destroy) (void *key);
     unsigned long (*key_to_index) (void *key);
     int (*item_has_key) (void *item, void *key);
};

that takes care to pick up a key_duplicate() and key_destroy() function of the 
user, so it can duplicate the keys for the dict usage and also has a function to 
destroy the keys when dict destruction takes place.

Well observed... Actually I used it on unsigned long which are allocated in a 
parallel structure, so there was no duplication or destroy needed.

A user that wants this simply creates the dict with NULL values for 
.key_duplicate and .key_destroy.

> 3)
> +List *gw_dict_keys(gw_dict_t *dict)
> +{
> +    List *list;
> +    Item *item;
> +    long i, j;
> +
> +    list = gwlist_create();
> +
> +    lock(dict);
> +    for (i = 0; i < dict->size; ++i) {
> +        if (dict->tab[i] == NULL)
> +            continue;
> +        for (j = 0; j < gwlist_len(dict->tab[i]); ++j) {
> +            item = gwlist_get(dict->tab[i], j);
> +            gwlist_append(list, octstr_duplicate(item->key));
> 
> why octstr_duplicate? key is not Octstr anymore... copy&paste ;)
> for gw_dict_keys we need duplicate function for key probably in  
> gw_dict_ops_t...

yep, see above... stupid copy&paste.

The revised version is attached.

Stipe

-------------------------------------------------------------------
Kölner Landstrasse 419
40589 DÃŒsseldorf, NRW, Germany

tolj.org system architecture      Kannel Software Foundation (KSF)
http://www.tolj.org/              http://www.kannel.org/

mailto:st_{at}_tolj.org           mailto:stolj_{at}_kannel.org
-------------------------------------------------------------------
gw-dict.patch (text/plain, 22.9 KB)
### Eclipse Workspace Patch 1.0
#P gateway-cvs-head
Index: gwlib/gw-dict.h
===================================================================
RCS file: gwlib/gw-dict.h
diff -N gwlib/gw-dict.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gwlib/gw-dict.h	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,205 @@
+/* ==================================================================== 
+ * The Kannel Software License, Version 1.0 
+ * 
+ * Copyright (c) 2001-2007 Kannel Group  
+ * Copyright (c) 1998-2001 WapIT Ltd.   
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. 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. 
+ * 
+ * 3. The end-user documentation included with the redistribution, 
+ *    if any, must include the following acknowledgment: 
+ *       "This product includes software developed by the 
+ *        Kannel Group (http://www.kannel.org/)." 
+ *    Alternately, this acknowledgment may appear in the software itself, 
+ *    if and wherever such third-party acknowledgments normally appear. 
+ * 
+ * 4. The names "Kannel" and "Kannel Group" must not be used to 
+ *    endorse or promote products derived from this software without 
+ *    prior written permission. For written permission, please  
+ *    contact [email protected]. 
+ * 
+ * 5. Products derived from this software may not be called "Kannel", 
+ *    nor may "Kannel" appear in their name, without prior written 
+ *    permission of the Kannel Group. 
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 KANNEL GROUP OR ITS 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. 
+ * ==================================================================== 
+ * 
+ * This software consists of voluntary contributions made by many 
+ * individuals on behalf of the Kannel Group.  For more information on  
+ * the Kannel Group, please see <http://www.kannel.org/>. 
+ * 
+ * Portions of this software are based upon software originally written at  
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.  
+ */ 
+
+/*
+ * gw-dict.h - hash lookup data structure using generic struct as key
+ *
+ * This data structure manages a hash table for void* objects and it's 
+ * associated key type is independant from this implemenation. We pass
+ * also void* for the key type and within typedef struct gw_dict_ops_t we
+ * define the three functions that deal with the key type explicitely.
+ * 
+ * Stipe Tolj
+ * Based on Lars Wirzenius's gwlib/dict.[ch] 
+ * Based on code by Tuomas Luttinen
+ */
+
+#ifndef GW_DICT_H
+#define GW_DICT_H
+
+typedef struct gw_dict gw_dict_t;
+
+
+/*
+ * To achive the void* key type independance, ie. against a static way to
+ * implement the key as unsigned long or Ocstr we use the following typedef
+ * gw_dict_ops_t that defines the three functions that deal with the type
+ * implementation of the stored void* item and the void* key.
+ * 
+ *   .destroy       - function applied to all void* item in the hash when 
+ *                    the dict is beeing destroyed
+ *   .key_duplicate - function that duplicates the key struct for the dict,
+ *                    if NULL the passed pointer is used
+ *   .key_destroy   - function that destroys a key struct in the dict item,
+ *                    if NULL no destruction is called on the key struct
+ *   .key_to_index  - function mapping a given void* key to a unsigned long
+ *                    that is used to calculate the hash table position
+ *   .item_has_key  - function used for gwlist_search() as compare function to
+ *                    give a result if the given key matches the stored item
+ * 
+ * So how is this used in the user code? We give here two examples for the 
+ * void *key handling functions:
+ * 
+ * Key is a Octstr*:
+ * 
+ *   static unsigned long key_to_index(void *key)
+ *   {
+ *      return octstr_hash_key((Octstr*) key);
+ *   }
+ *
+ *   static int item_has_key(void *item, void *key)
+ *   {
+ *      return octstr_compare((Octstr*) item, (Octstr*) key) == 0;
+ *   }
+ * 
+ * Key is a unisgned long:
+ * 
+ *   static unsigned long key_to_index(void *key)
+ *   {
+ *      unsigned long *k = (unsigned long*) key;
+ *      return *k;
+ *   }
+ * 
+ *   static int item_has_key(void *item, void *key)
+ *   {
+ *      unsigned long *i = (unsigned long*) item;
+ *      unsigned long *k = (unsigned long*) key;
+ *      return *i == *k;
+ *   }
+ */
+typedef struct gw_dict_ops gw_dict_ops_t;
+
+struct gw_dict_ops {
+    void (*destroy) (void *item);
+    void *(*key_duplicate) (void *key);
+    void (*key_destroy) (void *key);
+    unsigned long (*key_to_index) (void *key);
+    int (*item_has_key) (void *item, void *key);
+};
+
+
+/*
+ * Create a gw_dict_t. `size_hint' gives an indication of how many different
+ * keys will be in the gw_dict_t at the same time, at most. This is used for
+ * performance optimization; things will work fine, though somewhat
+ * slower, even if it the number is exceeded. `ops' is a pointer to a strcut
+ * defining the above type handling functions. If `ops.destroy' is NULL, then 
+ * values are not destroyed by the gw_dict_t, they are just discarded.
+ * 
+ * Example, where we handle Octstr* as items:
+ * 
+ *   static gw_dict_ops_t ops = {
+ *      .destroy = (void (*)(void *))octstr_destroy,
+ *      .key_to_index = key_to_index,
+ *      .item_has_key = item_has_key
+ *   };
+ * 
+ *   ...
+ *   gw_dict_t *dict = gw_dict_create(100, &ops);
+ *   ...
+ */
+gw_dict_t *gw_dict_create(long size_hint, gw_dict_ops_t *ops);
+
+
+/*
+ * Destroy a gw_dict_ and all values in it.
+ */
+void gw_dict_destroy(gw_dict_t *dict);
+
+
+/*
+ * Put a new value into a gw_dict_t. If the same key existed already, the
+ * old value is destroyed. If `value' is NULL, the old value is destroyed
+ * and the key is removed from the gw_dict_t.
+ */
+void gw_dict_put(gw_dict_t *dict, void *key, void *value);
+
+
+/*
+ * Put a new value into a gw_dict_t. Return error, if the same key existed all-
+ * ready.
+ */
+int gw_dict_put_once(gw_dict_t *dict, void *key, void *value);
+
+
+/*
+ * Look up a value in a gw_dict_t. If there is no value corresponding to a 
+ * key, return NULL, otherwise return the value. The value will not
+ * be removed from the gw_dict_t.
+ */
+void *gw_dict_get(gw_dict_t *dict, void *key);
+
+
+/*
+ * Remove a value from a gw_dict_t without destroying it.
+ */
+void *gw_dict_remove(gw_dict_t *dict, void *key);
+
+
+/*
+ * Return the number of keys which currently exist in the gw_dict_t.
+ */
+long gw_dict_key_count(gw_dict_t *dict);
+
+
+/*
+ * Return a list of all the currently defined keys in the gw_dict_t. The
+ * caller must destroy the list.
+ */
+List *gw_dict_keys(gw_dict_t *dict);
+
+
+#endif
Index: test/test_gw_dict.c
===================================================================
RCS file: test/test_gw_dict.c
diff -N test/test_gw_dict.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ test/test_gw_dict.c	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,145 @@
+/* ==================================================================== 
+ * The Kannel Software License, Version 1.0 
+ * 
+ * Copyright (c) 2001-2005 Kannel Group  
+ * Copyright (c) 1998-2001 WapIT Ltd.   
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. 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. 
+ * 
+ * 3. The end-user documentation included with the redistribution, 
+ *    if any, must include the following acknowledgment: 
+ *       "This product includes software developed by the 
+ *        Kannel Group (http://www.kannel.org/)." 
+ *    Alternately, this acknowledgment may appear in the software itself, 
+ *    if and wherever such third-party acknowledgments normally appear. 
+ * 
+ * 4. The names "Kannel" and "Kannel Group" must not be used to 
+ *    endorse or promote products derived from this software without 
+ *    prior written permission. For written permission, please  
+ *    contact [email protected]. 
+ * 
+ * 5. Products derived from this software may not be called "Kannel", 
+ *    nor may "Kannel" appear in their name, without prior written 
+ *    permission of the Kannel Group. 
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 KANNEL GROUP OR ITS 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. 
+ * ==================================================================== 
+ * 
+ * This software consists of voluntary contributions made by many 
+ * individuals on behalf of the Kannel Group.  For more information on  
+ * the Kannel Group, please see <http://www.kannel.org/>. 
+ * 
+ * Portions of this software are based upon software originally written at  
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.  
+ */ 
+
+/*
+ * test_gw_dict.c - test gw_dict_t objects
+ *
+ * Lars Wirzenius
+ * Stipe Tolj
+ */
+
+
+#include "gwlib/gwlib.h"
+#include "gwlib/gw-dict.h"
+
+#define HUGE_SIZE 200000
+
+
+/*
+ * gw_dict operations for key type Octstr*
+ */
+
+static unsigned long key_to_index(void *key)
+{
+    return octstr_hash_key((Octstr*) key);
+}
+
+static int item_has_key(void *item, void *key)
+{
+    return octstr_compare((Octstr*) item, (Octstr*) key) == 0;
+}
+
+/* Required since octstr_duplicate() is a macro and we can't
+ * pass it as argument to the below struct of operations. */
+static Octstr *my_octstr_duplicate(Octstr *os)
+{
+    return octstr_duplicate(os);
+}
+
+static gw_dict_ops_t ops = {
+    .destroy = (void*)octstr_destroy,
+    .key_duplicate = (void*)my_octstr_duplicate,
+    .key_destroy = (void*)octstr_destroy,
+    .key_to_index = key_to_index,
+    .item_has_key = item_has_key
+};
+
+
+int main(void)
+{
+    gw_dict_t *dict;
+    Octstr *foo, *bar;
+    unsigned long i;
+     
+    gwlib_init();
+    
+    foo = octstr_imm("foo");
+    bar = octstr_imm("bar");
+    
+    debug("",0,"gw_dict simple test.");
+    dict = gw_dict_create(10, &ops);
+    gw_dict_put(dict, foo, bar);
+    info(0, "foo gives %s", octstr_get_cstr(gw_dict_get(dict, foo)));
+    if (gw_dict_key_count(dict) == 1)
+        info(0, "there is but one foo.");
+    else
+        error(0, "key count is %ld, should be 1.", gw_dict_key_count(dict));
+    gw_dict_destroy(dict);
+
+    debug("",0,"Dict extended/huge test.");
+    dict = gw_dict_create(HUGE_SIZE, &ops);
+    for (i = 1; i <= HUGE_SIZE; i++) {
+        unsigned long val;
+        Octstr *okey, *oval;
+        uuid_t id;
+        char key[UUID_STR_LEN + 1];
+        uuid_generate(id);
+        uuid_unparse(id, key);
+        val = gw_rand();
+        okey = octstr_create(key);
+        oval = octstr_format("%ld", val);
+        gw_dict_put(dict, okey, oval);
+    }
+    gwthread_sleep(5); 
+    if (gw_dict_key_count(dict) == HUGE_SIZE)
+        info(0, "ok, got %d entries in the dictionary.", HUGE_SIZE);
+    else
+        error(0, "key count is %ld, should be %d.", gw_dict_key_count(dict), HUGE_SIZE);
+    gw_dict_destroy(dict);
+
+    gwlib_shutdown();
+    return 0;
+}
Index: gwlib/gw-dict.c
===================================================================
RCS file: gwlib/gw-dict.c
diff -N gwlib/gw-dict.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gwlib/gw-dict.c	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,382 @@
+/* ==================================================================== 
+ * The Kannel Software License, Version 1.0 
+ * 
+ * Copyright (c) 2001-2007 Kannel Group  
+ * Copyright (c) 1998-2001 WapIT Ltd.   
+ * All rights reserved. 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ * 
+ * 2. 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. 
+ * 
+ * 3. The end-user documentation included with the redistribution, 
+ *    if any, must include the following acknowledgment: 
+ *       "This product includes software developed by the 
+ *        Kannel Group (http://www.kannel.org/)." 
+ *    Alternately, this acknowledgment may appear in the software itself, 
+ *    if and wherever such third-party acknowledgments normally appear. 
+ * 
+ * 4. The names "Kannel" and "Kannel Group" must not be used to 
+ *    endorse or promote products derived from this software without 
+ *    prior written permission. For written permission, please  
+ *    contact [email protected]. 
+ * 
+ * 5. Products derived from this software may not be called "Kannel", 
+ *    nor may "Kannel" appear in their name, without prior written 
+ *    permission of the Kannel Group. 
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 KANNEL GROUP OR ITS 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. 
+ * ==================================================================== 
+ * 
+ * This software consists of voluntary contributions made by many 
+ * individuals on behalf of the Kannel Group.  For more information on  
+ * the Kannel Group, please see <http://www.kannel.org/>. 
+ * 
+ * Portions of this software are based upon software originally written at  
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.  
+ */ 
+
+/*
+ * gw-dict.c - hash lookup data structure using generic struct as key
+ *
+ * This data structure manages a hash table for void* objects and it's 
+ * associated key type is independant from this implemenation. We pass
+ * also void* for the key type and within typedef struct gw_dict_ops_t we
+ * define the three functions that deal with the key type explicitely.
+ * 
+ * Stipe Tolj
+ * Based on Lars Wirzenius's gwlib/dict.[ch] 
+ * Based on code by Tuomas Luttinen
+ */
+
+#include "gwlib.h"
+#include "gw-dict.h"
+
+
+/*
+ * The dictionary itself is a very simple hash table.
+ * `tab' is an array of Lists of Items, in which empty Lists may be
+ * represented as NULL.  `size' is the number of elements allocated
+ * for the array, and `key_count' is the number of Items currently
+ * in the table.  `key_count' is kept up to date by the put and remove
+ * functions, and is used to make gw_dict_key_count() faster.
+ */
+struct gw_dict {
+    List **tab;
+    long size;
+    long key_count;
+    gw_dict_ops_t *ops;
+    Mutex *lock;
+};
+
+
+/*
+ * The hash table stores key/value -pairs in a List.
+ */
+typedef struct Item Item;
+
+struct Item {
+    void *key;
+    void *value;
+    gw_dict_t *dict;
+};
+
+
+static Item *item_create(void *key, void *value, gw_dict_t *dict)
+{
+    Item *item;
+    
+    gw_assert(dict != NULL);
+    
+    item = gw_malloc(sizeof(*item));
+    item->key = dict->ops->key_duplicate != NULL ? 
+        dict->ops->key_duplicate(key) : key;
+    item->value = value;
+    item->dict = dict;
+    return item;
+}
+
+
+static void item_destroy(Item *item)
+{
+    gw_assert(item != NULL && item->dict->ops != NULL);
+    
+    if (item->dict->ops->key_destroy != NULL)
+        item->dict->ops->key_destroy(item->key);
+    gw_free(item);
+}
+
+
+static void lock(gw_dict_t *dict)
+{
+    mutex_lock(dict->lock);
+}
+
+
+static void unlock(gw_dict_t *dict)
+{
+    mutex_unlock(dict->lock);
+}
+
+
+/*
+ * A wrapper arround the given .item_has_key in the gw_dict_ops_t
+ * struct. Needed to ensure Item struct has not to be in public
+ * scope defined.
+ */
+static int item_has_key(void *item, void *key)
+{
+    Item *i = item;
+    gw_dict_t *d = i->dict;
+    return d->ops->item_has_key((void*)i->key, key);
+}
+
+
+static int handle_null_value(gw_dict_t *dict, void *key, void *value)
+{
+    if (value == NULL) {
+        value = gw_dict_remove(dict, key);
+	if (dict->ops->destroy != NULL)
+	    dict->ops->destroy(value);
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static int dict_put_true(gw_dict_t *dict, void *key, void *value)
+{
+    Item *p;
+    long i;
+    int item_unique;
+
+    item_unique = 0;
+    lock(dict);
+    i = dict->ops->key_to_index(key) % dict->size;
+
+    if (dict->tab[i] == NULL) {
+        dict->tab[i] = gwlist_create();
+        p = NULL;
+    } else {
+        p = gwlist_search(dict->tab[i], key, item_has_key);
+    }
+
+    if (p == NULL) {
+    	p = item_create(key, value, dict);
+        gwlist_append(dict->tab[i], p);
+        dict->key_count++;
+        item_unique = 1;
+    } else {
+    	if (dict->ops->destroy != NULL)
+    	    dict->ops->destroy(value);
+        item_unique = 0;
+    }
+
+    unlock(dict);
+
+    return item_unique;
+}
+
+
+/********************************************************************
+ * Public functions
+ */
+
+gw_dict_t *gw_dict_create(long size_hint, gw_dict_ops_t *ops)
+{
+    gw_dict_t *dict;
+    long i;
+    
+    dict = gw_malloc(sizeof(*dict));
+
+    /*
+     * Hash tables tend to work well until they are fill to about 50%.
+     * So we pre-allocate the double space in order to have enough room.
+     */
+    dict->size = size_hint * 2;
+
+    dict->tab = gw_malloc(sizeof(dict->tab[0]) * dict->size);
+    for (i = 0; i < dict->size; ++i)
+    	dict->tab[i] = NULL;
+    dict->lock = mutex_create();
+    dict->ops = ops;
+    dict->key_count = 0;
+    
+    return dict;
+}
+
+
+void gw_dict_destroy(gw_dict_t *dict)
+{
+    long i;
+    Item *p;
+    
+    if (dict == NULL)
+        return;
+
+    for (i = 0; i < dict->size; ++i) {
+        if (dict->tab[i] == NULL)
+            continue;
+
+        while ((p = gwlist_extract_first(dict->tab[i])) != NULL) {
+            if (dict->ops->destroy != NULL)
+                dict->ops->destroy(p->value);
+            item_destroy(p);
+        }
+        gwlist_destroy(dict->tab[i], NULL);
+    }
+    mutex_destroy(dict->lock);
+    gw_free(dict->tab);
+    gw_free(dict);
+}
+
+
+void gw_dict_put(gw_dict_t *dict, void *key, void *value)
+{
+    long i;
+    Item *p;
+
+    if (value == NULL) {
+        value = gw_dict_remove(dict, key);
+        if (dict->ops->destroy != NULL)
+            dict->ops->destroy(value);
+        return;
+    }
+
+    lock(dict);
+    i = dict->ops->key_to_index(key) % dict->size;
+    if (dict->tab[i] == NULL) {
+        dict->tab[i] = gwlist_create();
+        p = NULL;
+    } else
+        p = gwlist_search(dict->tab[i], key, item_has_key);
+    if (p == NULL) {
+    	p = item_create(key, value, dict);
+        gwlist_append(dict->tab[i], p);
+        dict->key_count++;
+    } else {
+        if (dict->ops->destroy != NULL)
+            dict->ops->destroy(p->value);
+        p->value = value;
+    }
+    unlock(dict);
+}
+
+
+int gw_dict_put_once(gw_dict_t *dict, void *key, void *value)
+{
+    int ret;
+
+    ret = 1;
+    if (handle_null_value(dict, key, value))
+        return 1;
+    if (dict_put_true(dict, key, value)) {
+        ret = 1;
+    } else {
+        ret = 0;
+    }
+    return ret;
+}
+
+
+void *gw_dict_get(gw_dict_t *dict, void *key)
+{
+    long i;
+    Item *p;
+    void *value;
+
+    lock(dict);
+    i = dict->ops->key_to_index(key) % dict->size;
+    if (dict->tab[i] == NULL)
+        p = NULL;
+    else
+        p = gwlist_search(dict->tab[i], key, item_has_key);
+    if (p == NULL)
+    	value = NULL;
+    else
+    	value = p->value;
+    unlock(dict);
+    return value;
+}
+
+
+void *gw_dict_remove(gw_dict_t *dict, void *key)
+{
+    long i;
+    Item *p;
+    void *value;
+    List *list;
+
+    lock(dict);
+    i = dict->ops->key_to_index(key) % dict->size;
+    if (dict->tab[i] == NULL)
+        list = NULL;
+    else
+        list = gwlist_extract_matching(dict->tab[i], key, item_has_key);
+    gw_assert(list == NULL || gwlist_len(list) == 1);
+    if (list == NULL)
+    	value = NULL;
+    else {
+        p = gwlist_get(list, 0);
+        gwlist_destroy(list, NULL);
+        value = p->value;
+        item_destroy(p);
+        dict->key_count--;
+    }
+    unlock(dict);
+    return value;
+}
+
+
+long gw_dict_key_count(gw_dict_t *dict)
+{
+    long result;
+
+    lock(dict);
+    result = dict->key_count;
+    unlock(dict);
+
+    return result;
+}
+
+
+List *gw_dict_keys(gw_dict_t *dict)
+{
+    List *list;
+    Item *item;
+    long i, j;
+    
+    list = gwlist_create();
+
+    lock(dict);
+    for (i = 0; i < dict->size; ++i) {
+        if (dict->tab[i] == NULL)
+            continue;
+        for (j = 0; j < gwlist_len(dict->tab[i]); ++j) {
+            item = gwlist_get(dict->tab[i], j);
+            gwlist_append(list, item->key);
+        }
+    }
+    unlock(dict);
+    
+    return list;
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.