#include #include "dat.h" #include "fn.h" /* * A byte trie over UTF-8 keys. Maps need prefix matches while composing; * dictionaries need exact matches; both are small enough for one structure. * Node 0 is the root. */ static int newnode(Trie *t) { int i; if(t->n == t->cap){ t->cap *= 2; t->nodes = erealloc(t->nodes, t->cap * sizeof(Tnode)); } 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; } Trie* trienew(void) { Trie *t; t = emalloc(sizeof(*t)); t->cap = 1024; t->nodes = emalloc(t->cap * sizeof(Tnode)); t->n = 0; newnode(t); return t; } void trieput(Trie *t, char *key, int klen, char *val, int vlen) { int ni, ci, i; ni = 0; 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(vlen + 1); memmove(t->nodes[ni].val, val, vlen); t->nodes[ni].val[vlen] = '\0'; t->nodes[ni].vlen = vlen; } static char* readline(Biobuf *b, char *path) { char *line; errno = 0; line = Brdstr(b, '\n', 1); if(errno != 0) die("can't read %s: %r", path); return line; } /* * A file holds "keyvalue" lines; ';' starts a comment. Keys and each * space-separated word of a value must fit a Str, which is what lookups * hand back. */ Trie* trieopen(char *path) { Trie *t; Biobuf *b; Str s; char *e, *line, *p, *tab; int len, lineno; b = Bopen(path, OREAD); if(b == nil) die("can't open %s: %r", path); t = trienew(); for(lineno = 1; (line = readline(b, path)) != nil; lineno++){ len = Blinelen(b); if(memchr(line, '\0', len) != nil) die("%s:%d: NUL byte", path, lineno); if(len > 0 && line[len-1] == '\r') line[--len] = '\0'; if(len == 0 || line[0] == ';'){ free(line); continue; } tab = memchr(line, '\t', len); if(tab == nil || tab == line || tab == line+len-1 || memchr(tab+1, '\t', line+len-(tab+1)) != nil) die("%s:%d: expected key, tab, value", path, lineno); if(!sinit(&s, line, tab-line)) die("%s:%d: invalid or oversized key", path, lineno); for(p = tab+1; p < line+len; p = e+1){ e = memchr(p, ' ', line+len-p); if(e == nil) e = line+len; if(!sinit(&s, p, e-p)) die("%s:%d: invalid or oversized value", path, lineno); } trieput(t, line, tab-line, tab+1, line+len-(tab+1)); free(line); } if(Bterm(b) < 0) die("can't close %s: %r", path); 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); } /* A nil trie is an unloaded map: every key misses. */ int trielookup(Trie *t, Str *key, char **val, int *vlen) { char buf[Maxutf]; int i, klen, ni; *val = nil; *vlen = 0; if(t == nil) return TrieMiss; klen = stoutf(key, buf, sizeof buf); ni = 0; for(i = 0; i < klen; i++){ ni = find(t, ni, buf[i]); if(ni < 0) return TrieMiss; } if(t->nodes[ni].val == nil) return TriePrefix; *val = t->nodes[ni].val; *vlen = t->nodes[ni].vlen; return TrieExact; }