/* Copyright 2006 Garrett Rooney. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "etl_hash.h" #include "tree.h" #include #include /* XXX need to replace this with a real hash table implementation. * * XXX unify this with the code that drives the hash variables. */ struct etl_hash_t { etl_tree_node_t *tree; }; typedef struct { const char *key; const void *val; } node_t; static int compare(void *a, void *b) { node_t *one = a, *two = b; return strcmp(one->key, two->key); } typedef struct { etl_hash_elt_destroy_t destroy; void *baton; } destructor_baton_t; static void destroy(void *content, void *baton) { destructor_baton_t *db = baton; node_t *n = content; free((char *) n->key); if (db->destroy) db->destroy((void *) n->val, db->baton); free(n); } static etl_tree_vtbl_t vtable = { compare, destroy }; etl_hash_t * etl_hash_create() { return calloc(1, sizeof(etl_hash_t)); } void etl_hash_destroy(etl_hash_t *hash, etl_hash_elt_destroy_t destroy, void *b) { if (! hash) return; if (hash->tree) { destructor_baton_t baton; baton.destroy = destroy; baton.baton = b; etl_tree_destroy(hash->tree, &baton); } free(hash); } /* Returns the existing value, if there is one. */ void * etl_hash_set(etl_hash_t *hash, const char *key, const void *val) { node_t *node = calloc(1, sizeof(*node)); node->key = strdup(key); node->val = val; if (! hash->tree) { hash->tree = etl_tree_create(node, &vtable); return NULL; } else { bool added = etl_tree_insert(hash->tree, node); if (! added) { etl_tree_node_t *current = etl_tree_find(hash->tree, node); void *rv; free((char *) node->key); free(node); rv = (void *) ((node_t *) current->content)->val; ((node_t *) current->content)->val = val; return rv; } else { return NULL; } } } void * etl_hash_get(etl_hash_t *hash, const char *key) { if (! hash->tree) { return NULL; } else { etl_tree_node_t *n; node_t content; content.key = key; content.val = NULL; n = etl_tree_find(hash->tree, &content); if (! n) return NULL; else return (void *) ((node_t *) n->content)->val; } } typedef struct { void (*callback)(const char *key, void *val, void *baton); void *baton; } tree_traverse_baton_t; static void tree_traverse_callback(etl_tree_node_t *node, void *b) { tree_traverse_baton_t *baton = b; node_t *n = node->content; baton->callback(n->key, (void *) n->val, baton->baton); } void etl_hash_traverse(etl_hash_t *hash, void (*callback)(const char *key, void *val, void *baton), void *b) { tree_traverse_baton_t baton; if (! hash || ! hash->tree) return; baton.callback = callback; baton.baton = b; etl_tree_traverse(hash->tree, tree_traverse_callback, &baton); } typedef struct { etl_hash_t *hash; etl_hash_copy_val_t copy; void *baton; } set_var_baton_t; static void set_var(const char *key, void *v, void *b) { set_var_baton_t *baton = b; void *val; if (baton->copy) val = baton->copy(v, b); else val = v; etl_hash_set(baton->hash, key, val); } etl_hash_t * etl_hash_copy(etl_hash_t *hash, etl_hash_copy_val_t copy, void *b) { etl_hash_t *rv = etl_hash_create(); set_var_baton_t baton; baton.hash = rv; baton.copy = copy; baton.baton = b; etl_hash_traverse(hash, set_var, &baton); return rv; }