1
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
6.7 KiB
C

/*
* trie.c: Trie implementation for storing values
*/
#include <string.h>
#include <syslog.h>
#include <stdio.h>
#include <stdlib.h>
#include "trie.h"
#include "config.h"
#include "task.h"
enum dfs_list {
VERTICES, VARIABLES
};
static struct trie *trie_base = NULL;
static int indexof(char c) {
// ASCII only!
if ('A' <= c && c <= 'Z') {
return (c - 'A');
} else if ('0' <= c && c <= '9') {
return (c - '0' + 26);
} else if (c == '_') {
return 36;
} else {
// Something weird happened
syslog(cfg_log_facility|LOG_NOTICE, "trie: weird character: %c", c);
return -1;
}
}
static char charof(int i) {
//ASCII again
if (i <= 25){
return ('A' + i);
} else if (26 <= i && i <= 35) {
return ('0' + i - 26);
} else if (i == 36) {
return '_';
} else {
syslog(cfg_log_facility|LOG_NOTICE, "trie: weird character index: %d", i);
return '\0';
}
}
static struct trie *trie_find_vertex(char string[], struct trie *vertex, bool create) {
if (vertex == NULL) {
if (create == false) {
return NULL;
} else {
vertex = (struct trie *) calloc(1, sizeof(struct trie));
if (vertex == NULL) {
syslog(cfg_log_facility | LOG_ERR, "trie: could not allocate space: %m");
return NULL;
}
}
}
if (string[0] == '\0') {
return vertex;
} else {
int chld_index = indexof(string[0]);
if (chld_index == -1) {
return NULL;
} else if (vertex -> children[chld_index] == NULL) {
if (create == false) {
return NULL;
} else {
vertex -> children[chld_index] = (struct trie *) calloc(1, sizeof(struct trie));
if (vertex -> children[chld_index] == NULL) {
syslog(cfg_log_facility | LOG_ERR, "trie: could not allocate space: %m");
return NULL;
}
}
}
return trie_find_vertex(string+1, vertex->children[chld_index], create);
}
}
static bool trie_redefine (char string[], int64_t val, bool def){
if (trie_base == NULL) {
trie_base = (struct trie *) calloc(1, sizeof(struct trie));
if(trie_base == NULL) {
syslog(cfg_log_facility | LOG_ERR, "trie: could not allocate any space: %m");
return false;
}
}
struct trie *v = trie_find_vertex(string, trie_base, def);
if (v == NULL && def == true) {
//Something is wrong, but it is already logged
return false;
} else {
v -> val = val;
v -> def = def;
//Recalculate tasks
if (v -> tasks != NULL) {
for (uint64_t i = 0; i < v -> tasks -> elems; i++) {
if (v -> tasks -> arr[i] != NULL) {
uint64_t id = *(uint64_t *)(v -> tasks -> arr[i]);
task_recalc(id);
}
}
}
return true;
}
}
bool trie_set(char string[], int64_t val) {
return trie_redefine(string, val, true);
}
bool trie_unset(char string[]) {
return trie_redefine(string, 0, false);
}
struct trie_retval trie_lookup(char string[]) {
struct trie *v = trie_find_vertex(string, trie_base, false);
if (v == NULL) {
return (struct trie_retval) {0, false, NULL};
} else {
return (struct trie_retval) {v -> val, v -> def, v->tasks};
}
}
bool trie_load(const char fn[]) {
FILE *f = fopen(fn, "r");
if (f == NULL) {
syslog(cfg_log_facility | LOG_WARNING, "trie: could not load trie from file: %m");
return false;
}
//FIXME: This is bad.
int64_t val;
char name[cfg_var_name_max_len+1];
int status;
while ((status = fscanf(f, "%s %ld\n", name, &val)) != EOF) {
if (status != 2) {
syslog(cfg_log_facility | LOG_ERR, "trie: fscanf matched bad number of items: %d", status);
return false;
}
if(trie_set(name, val) == false) {
syslog(cfg_log_facility | LOG_WARNING, "trie: setting a variable failed: %s = %ld", name, val);
}
}
return true;
}
static bool trie_dfs(char prefix[], uint64_t prefixlen, struct grow *list, struct trie *vertex, enum dfs_list style) {
if (vertex == NULL) {// Very special case;
return true;
} else {
bool retval = true;
if (style == VARIABLES) {
if (prefixlen > cfg_var_name_max_len) {
syslog(cfg_log_facility | LOG_NOTICE, "trie: dfs: too long variable name in trie");
return false;
}
if (vertex -> def) {
prefix[prefixlen] = '\0';
// Add to list
struct trie_list *l = malloc(sizeof(struct trie_list));
if (l == NULL) {
syslog(cfg_log_facility|LOG_WARNING, "trie: dfs: cannot create list element: %m");
return false;
}
l -> val = vertex -> val;
l -> name = malloc((prefixlen + 1) * sizeof(char));
strcpy(l->name, prefix);
retval &=grow_push(l, list);
}
} else if (style == VERTICES) {
// Add pointers to all vertices
retval &= grow_push(vertex, list);
}
for (int i = 0; i<37; i++) {
if (vertex -> children[i] != NULL) {
if (style == VARIABLES) {
prefix[prefixlen] = charof(i);
}
retval &= trie_dfs(prefix, prefixlen + 1, list, vertex -> children[i], style);
}
}
return retval;
}
}
bool trie_save(const char fn[]) {
FILE *f = fopen(fn, "w");
if (f == NULL) {
syslog(cfg_log_facility | LOG_CRIT, "trie: could not open file to save trie in: %m");
return false;
}
// DFS
struct grow *list = grow_init(true);
char prefix[cfg_var_name_max_len +1];
prefix[cfg_var_name_max_len +1] = '\0';
trie_dfs(prefix, 0, list, trie_base, VARIABLES);
for (uint64_t i = 0; i < list -> elems; i++) {
struct trie_list l = *(struct trie_list *)(list -> arr[i]);
fprintf(f, "%s %ld\n", l.name, l.val);
}
grow_drop(list);
return true;
}
struct grow *trie_list(void) {
struct grow *list = grow_init(true);
char prefix[cfg_var_name_max_len +1];
prefix[cfg_var_name_max_len +1] = '\0';
trie_dfs(prefix, 0, list, trie_base, VARIABLES);
return list;
}
bool trie_assoc(char var[], uint64_t task) {
struct trie *v = trie_find_vertex(var, trie_base, true);
if (v == NULL) {
return false;
} else {
if (v -> tasks == NULL) {
v -> tasks = grow_init(true);
if (v->tasks == NULL) {
return false;
}
}
uint64_t *p_task = malloc(sizeof(uint64_t));
if (p_task == NULL) {
syslog(cfg_log_facility | LOG_ERR, "trie: Could not associate: %m");
return false;
}
*p_task = task;
return grow_push(p_task, v->tasks);
}
}
bool trie_unassoc(char var[], uint64_t task) {
struct trie *v = trie_find_vertex(var, trie_base, false);
if (v == NULL) {
return false; //Was not associated
} else {
if (v -> tasks == NULL) {
return false; //Was not associated
} else {
bool retval = false;
for (uint64_t i = 0; i < v->tasks->elems; i++) {
uint64_t *t = (uint64_t *)(v->tasks->arr[i]);
if (t == NULL) {
continue;
} else if (*t == task) {
// Erase it
v -> tasks -> active_elems--;
free(t);
v->tasks->arr[i] = NULL;
retval = true;
}
}
if (v -> tasks -> active_elems == 0) {
grow_drop(v->tasks);
}
return retval;
}
}
}
void trie_drop (void) {
struct grow *list = grow_init(true);
trie_dfs("", 0, list, trie_base, VERTICES);
grow_drop(list);
return;
}