Generic dictionary type for later use.
Implements an ordered, searchable collection of string-value pairs, which I will later use to specify replacement files against a template ODT document.
This commit is contained in:
251
src/dict.c
Normal file
251
src/dict.c
Normal file
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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
|
||||
61
src/dict.h
Normal file
61
src/dict.h
Normal file
@@ -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) */
|
||||
@@ -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); } \
|
||||
|
||||
Reference in New Issue
Block a user