Files
strans/trie.c

170 lines
3.1 KiB
C

#include <errno.h>
#include <limits.h>
#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;
}