/* * trie.c: Trie implementation for storing values */ #include #include #include #include #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; }