/* * dict.c * Copyright © 2016 David A. Baer * 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. Neither the name of the organization nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY David A. Baer ''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 David A. Baer 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 #include #include #include "stack.h" #include "dict.h" struct dict_node { char* key; void* value; struct dict_node *parent, *left, *right; }; struct dict { struct dict_node* head; unsigned int size; uint8_t options; }; DEFINE_STACK(struct dict_node*, DictTraversalStack); struct dict_iter { DictTraversalStack stack; }; struct dict* dict_init() { struct dict* ptr = malloc(sizeof(struct dict)); if (ptr == NULL) { perror("Could not allocate dict"); exit(1); } memset(ptr, 0, sizeof(struct dict)); return ptr; } uint8_t dict_get_options(const struct dict* d) { return d->options; } void dict_set_options(struct dict* d, uint8_t options) { d->options = options; } struct dict_node* dict_node_create(const char *key, void* value) { struct dict_node* ptr = malloc(sizeof(struct dict_node)); if (ptr == NULL) { perror("Could not allocate dict_node"); exit(1); } memset(ptr, 0, sizeof(struct dict_node)); ptr->key = strdup(key); ptr->value = value; return ptr; } int dict_add(struct dict* d, const char* key, void* value) { struct dict_node* ptr = NULL, *new_node = NULL; int cmp = 0; /* case: empty dictionary */ if (d->head == NULL) { d->head = dict_node_create(key, value); return DICT_OK; } /* search for proper position */ ptr = d->head; cmp = strcmp(key, ptr->key); while (cmp != 0) { if (cmp < 0) { if (ptr->left == NULL) { ptr->left = dict_node_create(key, value); ptr->left->parent = ptr; d->size++; return DICT_OK; } ptr = ptr->left; } else { /* cmp > 0 */ if (ptr->right == NULL) { ptr->right = dict_node_create(key, value); ptr->right->parent = ptr; d->size++; return DICT_OK; } ptr = ptr->right; } cmp = strcmp(key, ptr->key); } /* couldn't add - duplicate key */ return DICT_DUPLICATE_KEY; } int dict_find(const struct dict* d, const char* key, void** p_value) { struct dict_node* ptr = d->head; int cmp = 0; while (ptr != NULL) { cmp = strcmp(key, ptr->key); if (cmp == 0) { if (p_value) *p_value = ptr->value; return DICT_OK; } else if (cmp < 0) { ptr = ptr->left; } else { /* cmp > 0 */ ptr = ptr->right; } } return DICT_NOT_FOUND; } const char* dict_iter_key(const struct dict_iter* i) { return STACK_HEAD(i->stack)->key; } void* dict_iter_value(struct dict_iter* i) { return STACK_HEAD(i->stack)->value; } struct dict_iter* dict_iter_begin(struct dict* d) { struct dict_iter* i = malloc(sizeof(struct dict_iter)); if (i == NULL) { perror("Could not allocate new dict_iter"); exit(1); } struct dict_node* ptr = NULL; INIT_STACK(i->stack); ptr = d->head; while (ptr != NULL) { PUSH_STACK(DictTraversalStack, i->stack, ptr); ptr = ptr->left; } return i; } void dict_iter_next(struct dict_iter* i) { struct dict_node* ptr = STACK_HEAD(i->stack)->right; /*printf("dict_iter_next: stack height=%d\n", STACK_HEIGHT(i->stack));*/ POP_STACK(DictTraversalStack, i->stack); while (ptr != NULL) { PUSH_STACK(DictTraversalStack, i->stack, ptr); ptr = ptr->left; } } int dict_iter_done(const struct dict_iter* i) { return STACK_HEIGHT(i->stack) == 0; } void dict_iter_free(struct dict_iter* i) { DESTROY_STACK(DictTraversalStack, i->stack); free(i); } void dict_free_cb(struct dict* d, void (*callback)(const char*, void*)) { /* like iteration above, only post-order */ NEW_STACK(DictTraversalStack, stack); PUSH_STACK(DictTraversalStack, stack, d->head); while (STACK_HEIGHT(stack) > 0) { struct dict_node* ptr = STACK_HEAD(stack); if (ptr->right != NULL) { PUSH_STACK(DictTraversalStack, stack, ptr->right); } if (ptr->left != NULL) { PUSH_STACK(DictTraversalStack, stack, ptr->left); } if ((ptr->left == NULL) && (ptr->right == NULL)) { if (ptr->parent != NULL) { if (ptr == ptr->parent->left) { ptr->parent->left = NULL; } else { ptr->parent->right = NULL; } } else { d->head = NULL; } if (callback) { callback(ptr->key, ptr->value); } free(ptr->key); free(ptr); POP_STACK(DictTraversalStack, stack); } } } void dict_free(struct dict* d) { dict_free_cb(d, NULL); } static void _dict_free_cb_free_value(const char* key, void* value) { free(value); } void dict_free_free_value(struct dict* d) { dict_free_cb(d, _dict_free_cb_free_value); } #ifdef TEST const char* test_keys[] = { "foo", "bar", "apples", "oranges", "bananas", NULL }; void mycb(const char* key, void* value) { printf("Freeing [%s] -> 0x%08llx\n", key, (uint64_t)value); } int main() { struct dict* d = dict_init(); struct dict_iter* i; int64_t n; dict_add(d, "apples", (void*)1); dict_add(d, "foo", (void*)3); dict_add(d, "monkeys", (void*)42); dict_add(d, "oranges", (void*)99); if (dict_add(d, "oranges", (void*)100) != DICT_OK) { printf("Failed to add duplicate (which is good)\n"); } printf("{ "); for (i = dict_iter_begin(d); !dict_iter_done(i); dict_iter_next(i)) { printf("\"%s\" : %lld, ", dict_iter_key(i), (int64_t)dict_iter_value(i)); } dict_iter_free(i); printf(" }\n"); for (ptr = test_keys; *ptr != NULL; ptr++) { if (dict_find(d, *ptr, (void**)&n) == DICT_OK) { printf("Key \"%s\" found: %lld\n", *ptr, n); } else { printf("Key \"%s\" not found.\n", *ptr); } } dict_free_cb(d, mycb); } #endif