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.
278 lines
6.7 KiB
C
278 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[]) {
|
|
bool retval;
|
|
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);
|
|
retval = false;
|
|
}
|
|
if(trie_set(name, val) == false) {
|
|
syslog(cfg_log_facility | LOG_WARNING, "trie: setting a variable failed: %s = %ld", name, val);
|
|
retval = false;
|
|
}
|
|
}
|
|
fclose(f);
|
|
return retval;
|
|
}
|
|
|
|
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);
|
|
fclose(f);
|
|
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_flush (void) {
|
|
struct grow *list = grow_init(true);
|
|
trie_dfs("", 0, list, trie_base, VERTICES);
|
|
grow_drop(list);
|
|
return;
|
|
}
|