#include #include #include "dat.h" #include "fn.h" static char* readline(Biobuf *b, char *path) { char *line; errno = 0; line = Brdstr(b, '\n', 1); if(errno != 0) die("can't read %s: %s", path, strerror(errno)); return line; } static int newnode(Trie *t) { int cap; int i; if(t->n >= t->cap){ if(t->cap > INT_MAX/2 || (ulong)t->cap > ULONG_MAX/(2*sizeof(Tnode))) die("map is too large"); cap = t->cap * 2; t->nodes = erealloc(t->nodes, (ulong)cap * sizeof(Tnode)); t->cap = cap; } i = t->n++; memset(&t->nodes[i], 0, sizeof(Tnode)); t->nodes[i].child = -1; t->nodes[i].sibling = -1; return i; } static int find(Trie *t, int ni, char c) { int pi; for(pi = t->nodes[ni].child; pi >= 0; pi = t->nodes[pi].sibling) if(t->nodes[pi].c == c) return pi; return -1; } static int add(Trie *t, int ni, char c) { int pi; pi = newnode(t); t->nodes[pi].c = c; t->nodes[pi].sibling = t->nodes[ni].child; t->nodes[ni].child = pi; return pi; } static void insert(Trie *t, char *key, int klen, char *val, int vlen) { int ni, ci; int i; ni = t->root; for(i = 0; i < klen; i++){ ci = find(t, ni, key[i]); if(ci < 0) ci = add(t, ni, key[i]); ni = ci; } free(t->nodes[ni].val); t->nodes[ni].val = emalloc((ulong)vlen + 1); memmove(t->nodes[ni].val, val, vlen); t->nodes[ni].val[vlen] = '\0'; t->nodes[ni].vlen = vlen; } Trie* trieopen(char *path) { Trie *t; Biobuf *b; char *line, *tab, *key, *val; Str s; int klen, vlen; b = Bopen(path, OREAD); if(b == nil) die("can't open %s: %s", path, strerror(errno)); t = emalloc(sizeof(*t)); t->cap = 1024; t->nodes = emalloc((ulong)t->cap * sizeof(Tnode)); t->n = 0; t->root = newnode(t); while((line = readline(b, path)) != nil){ vlen = Blinelen(b); if(memchr(line, '\0', vlen) != nil) die("NUL in map: %s", path); if(vlen > 0 && line[vlen-1] == '\r') line[--vlen] = '\0'; if(line[0] == '\0' || line[0] == ';'){ free(line); continue; } tab = memchr(line, '\t', vlen); if(tab == nil || tab == line || tab == line+vlen-1 || memchr(tab+1, '\t', line+vlen-(tab+1)) != nil) die("malformed map: %s", path); *tab = '\0'; key = line; klen = tab - line; if(!sinit(&s, key, klen)) die("invalid or oversized map key: %s", path); val = tab + 1; vlen = line + vlen - val; if(!sinit(&s, val, vlen)) die("invalid or oversized map value: %s", path); insert(t, key, klen, val, vlen); free(line); } if(Bterm(b) < 0) die("can't close %s: %s", path, strerror(errno)); return t; } void trieclose(Trie *t) { int i; if(t == nil) return; for(i = 0; i < t->n; i++) free(t->nodes[i].val); free(t->nodes); free(t); } int trielookup(Trie *t, char *key, int klen, char **val, int *vlen) { int ni; int i; if(val != nil) *val = nil; if(vlen != nil) *vlen = 0; if(t == nil || klen < 0 || (klen > 0 && key == nil)) return TrieMiss; ni = t->root; for(i = 0; i < klen; i++){ ni = find(t, ni, key[i]); if(ni < 0) return TrieMiss; } if(t->nodes[ni].val != nil){ if(val != nil) *val = t->nodes[ni].val; if(vlen != nil) *vlen = t->nodes[ni].vlen; return TrieExact; } return TriePrefix; }