diff --git a/src/dict.c b/src/dict.c new file mode 100644 index 0000000..697591e --- /dev/null +++ b/src/dict.c @@ -0,0 +1,251 @@ +/* + * 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) { + *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; + 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_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)); + } + 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 diff --git a/src/dict.h b/src/dict.h new file mode 100644 index 0000000..36d112f --- /dev/null +++ b/src/dict.h @@ -0,0 +1,61 @@ +/* + * dict.h + * 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. + * + */ +#ifndef _DICT_H +#define _DICT_H + +#define DICT_OK 0 +#define DICT_DUPLICATE_KEY 1 +#define DICT_NOT_FOUND 2 + +#define DICT_OPTION_REPLACE 1 + +struct dict; +struct dict_iter; + +struct dict* dict_init(); +uint8_t dict_get_options(const struct dict*); +void dict_set_options(struct dict*, uint8_t); +int dict_add(struct dict*, const char* key, void* value); +int dict_find(const struct dict*, const char* key, void** value); +void dict_free(struct dict*); +void dict_free_cb(struct dict*, void(*)(const char*, void*)); +void dict_free_free_value(struct dict*); +unsigned int dict_size(const struct dict*); + +/* + * a dict_iter keeps state for an in-order traversal of the dictionary + */ +const char* dict_iter_key(const struct dict_iter); +void* dict_iter_value(struct dict_iter); + +struct dict_iter dict_iter_begin(struct dict*); +void dict_iter_next(struct dict_iter*); +int dict_iter_done(const struct dict_iter); + +#endif /* !def(_DICT_H) */ diff --git a/src/stack.h b/src/stack.h index a9b1884..542e993 100644 --- a/src/stack.h +++ b/src/stack.h @@ -27,6 +27,11 @@ typedef struct { \ #define NEW_STACK(T, N) \ T N = (T) { .height = 0, .head = NULL } +#define INIT_STACK(N) { \ + (N).height = 0; \ + (N).head = NULL; \ +} + #define PUSH_STACK(T, S, E) { \ struct _##T##Node* n = (struct _##T##Node*)malloc(sizeof(struct _##T##Node)); \ if (!n) { perror ("Could not allocate space for new stack element."); exit(1); } \