diff options
| author | Franklin Wei <git@fwei.tk> | 2016-02-12 21:54:42 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2016-02-16 20:42:49 -0500 |
| commit | b110e7e0c519cc9575f8d224f0f75aca0d73946f (patch) | |
| tree | c3f33326a5e4822f2251e8d7370294096ab2eba4 /src/multimap.c | |
| parent | a006044fbcb3355f0fa063720e7c41f4971894a0 (diff) | |
| download | netcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.zip netcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.tar.gz netcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.tar.bz2 netcosm-b110e7e0c519cc9575f8d224f0f75aca0d73946f.tar.xz | |
support multiple objects sharing the same name
Diffstat (limited to 'src/multimap.c')
| -rw-r--r-- | src/multimap.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/src/multimap.c b/src/multimap.c new file mode 100644 index 0000000..d609d0f --- /dev/null +++ b/src/multimap.c @@ -0,0 +1,351 @@ +/* + * NetCosm - a MUD server + * Copyright (C) 2016 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "globals.h" + +#include "multimap.h" + +#define CHECK_SENTINEL(map) do{if(map && ((struct multimap_t*)map)->sentinel!=MULTIMAP_SENTINEL)error("hash/multimap mixing");}while(0); + +/* contains all the pairs using a key */ +struct multimap_node { + struct multimap_list *list; + size_t n_pairs; + struct multimap_t *map; +}; + +struct multimap_t { + char sentinel; + size_t total_pairs; + unsigned refcount; + + /* map of key->multimap nodes */ + void *hash_tab; + int (*compare_val)(const void *val_a, const void *val_b); + + void (*free_data)(void*); + void (*free_key)(void*); + void *(*dup_data)(void*); +}; + +static void free_node(void *ptr) +{ + struct multimap_node *node = ptr; + /* free the list */ + struct multimap_list *iter = node->list, *next; + while(iter) + { + next = iter->next; + if(node->map->free_data) + node->map->free_data(iter->val); + if(node->map->free_key) + node->map->free_key((void*)iter->key); + free(iter); + iter = next; + } + free(node); +} + +static void *dup_node(void *ptr) +{ + struct multimap_node *node = ptr; + struct multimap_node *ret = calloc(1, sizeof(*ret)); + memcpy(ret, node, sizeof(*ret)); + + struct multimap_list *iter = node->list, *last = NULL; + while(iter) + { + struct multimap_list *newpair = calloc(1, sizeof(*newpair)); + memcpy(newpair, iter, sizeof(*newpair)); + if(!last) + ret->list = iter; + else + last->next = iter; + last = newpair; + + iter = iter->next; + } + + return ret; +} + +void *multimap_init(size_t tabsz, + unsigned (*hash_fn)(const void *key), + int (*compare_key)(const void *key_a, const void *key_b), + int (*compare_val)(const void *val_a, const void *val_b)) +{ + struct multimap_t *ret = calloc(1, sizeof(struct multimap_t)); + ret->hash_tab = hash_init(tabsz, hash_fn, compare_key); + hash_setfreedata_cb(ret->hash_tab, free_node); + hash_setdupdata_cb(ret->hash_tab, dup_node); + ret->compare_val = compare_val; + ret->sentinel = MULTIMAP_SENTINEL; + ret->refcount = 1; + return ret; +} + +void multimap_free(void *ptr) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + if(!(--map->refcount)) + { + hash_free(map->hash_tab); + free(map); + } + } +} + +const struct multimap_list *multimap_lookup(void *ptr, const void *key, size_t *n_pairs) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + struct multimap_node *node = hash_lookup(map->hash_tab, key); + + if(!node) + { + if(n_pairs) + *n_pairs = 0; + return NULL; + } + + if(n_pairs) + *n_pairs = node->n_pairs; + + return node->list; + } + else + return NULL; +} + +bool multimap_insert(void *ptr, const void *key, const void *val) +{ + if(ptr) + { + struct multimap_t *map = ptr; + + CHECK_SENTINEL(map); + + struct multimap_node *node = hash_lookup(map->hash_tab, key); + if(!node) + { + node = calloc(1, sizeof(struct multimap_node)); + + node->map = map; + + node->list = calloc(1, sizeof(struct multimap_list)); + node->list->key = key; + node->list->val = (void*)val; + node->list->next = NULL; + + node->n_pairs = 1; + ++map->total_pairs; + + hash_insert(map->hash_tab, key, node); + + return true; + } + else + { + struct multimap_list *new = calloc(1, sizeof(struct multimap_list)); + new->key = key; + new->val = (void*)val; + new->next = node->list; + + ++node->n_pairs; + ++map->total_pairs; + + node->list = new; + + return false; + } + } + else + return false; +} + +size_t multimap_delete(void *ptr, const void *key, const void *val) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + + struct multimap_node *node = hash_lookup(map->hash_tab, key); + + if(!node) + return false; + /* iterate over the node's pairs and delete */ + size_t deleted = 0; + + struct multimap_list *last = NULL, *iter = node->list, *next;; + while(iter) + { + next = iter->next; + if(!map->compare_val(val, iter->val)) + { + if(last) + last->next = iter->next; + else + node->list = iter->next; + free(iter); + ++deleted; + --node->n_pairs; + --map->total_pairs; + } + else + last = iter; + iter = next; + } + + if(!node->n_pairs) + { + hash_remove(map->hash_tab, key); + } + + return deleted; + } + else + return 0; +} + +size_t multimap_delete_all(void *ptr, const void *key) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + + struct multimap_node *node = hash_lookup(map->hash_tab, key); + if(node) + { + size_t ret = node->n_pairs; + map->total_pairs -= ret; + + hash_remove(map->hash_tab, key); + + return ret; + } + /* fall through */ + } + return 0; +} + +const struct multimap_list *multimap_iterate(void *ptr, void **save, size_t *n_pairs) +{ + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + + struct multimap_node *node; + if(map) + node = hash_iterate(map->hash_tab, save, NULL); + else + node = hash_iterate(NULL, save, NULL); + + if(node) + { + if(n_pairs) + *n_pairs = node->n_pairs; + + return node->list; + } + else + return NULL; +} + +size_t multimap_size(void *ptr) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + return map->total_pairs; + } + else + return 0; +} + +void multimap_setfreedata_cb(void *ptr, void (*cb)(void *ptr)) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + + map->free_data = cb; + } +} + +void multimap_setdupdata_cb(void *ptr, void *(*cb)(void *ptr)) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + + map->dup_data = cb; + } +} + +void *multimap_dup(void *ptr) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + ++map->refcount; + + return map; + } + else + return NULL; +} + +void *multimap_copy(void *ptr) +{ + if(ptr) + { + struct multimap_t *map = ptr; + CHECK_SENTINEL(map); + + struct multimap_t *ret = calloc(1, sizeof(struct multimap_t)); + memcpy(ret, map, sizeof(*ret)); + + ret->hash_tab = hash_dup(map->hash_tab); + ret->refcount = 1; + + /* iterate and replace each node's *map pointer */ + void *map_ptr = ret->hash_tab, *save; + while(1) + { + struct multimap_node *node = hash_iterate(map_ptr, &save, NULL); + if(!node) + break; + map_ptr = NULL; + node->map = ret; + } + + return ret; + } + else + return NULL; +} |