fix(data): reject partial and malformed map data

This commit is contained in:
2026-08-14 23:09:55 +09:00
parent 38475318db
commit bfe83d0119
10 changed files with 220 additions and 125 deletions

7
dat.h
View File

@@ -87,6 +87,13 @@ struct Trie
int cap; int cap;
}; };
enum
{
TrieMiss,
TriePrefix,
TrieExact,
};
typedef struct Hmap Hmap; typedef struct Hmap Hmap;
struct Hmap struct Hmap
{ {

46
dict.c
View File

@@ -1,6 +1,19 @@
#include <errno.h>
#include "dat.h" #include "dat.h"
#include "fn.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;
}
void void
dictlookup(Dictreq *req, Dictres *res) dictlookup(Dictreq *req, Dictres *res)
{ {
@@ -33,7 +46,10 @@ dictlookup(Dictreq *req, Dictres *res)
sp = p; sp = p;
while(p < e && *p != ' ') while(p < e && *p != ' ')
p++; p++;
sinit(&tmp, sp, p - sp); if(!sinit(&tmp, sp, p - sp)){
res->nkouho = 0;
return;
}
if(req->lang == LangEMOJI || scmp(&tmp, &req->key) != 0) if(req->lang == LangEMOJI || scmp(&tmp, &req->key) != 0)
res->kouho[res->nkouho++] = tmp; res->kouho[res->nkouho++] = tmp;
if(p < e) if(p < e)
@@ -63,45 +79,49 @@ dictopen(char *path)
{ {
Hmap *h; Hmap *h;
Biobuf *b; Biobuf *b;
Str key; Str key, tmp;
char *line, *tab, *p, *e; char *line, *tab, *p, *e;
int len, lineno; int len, lineno;
b = Bopen(path, OREAD); b = Bopen(path, OREAD);
if(b == nil) if(b == nil)
die("can't open: %s", path); die("can't open %s: %s", path, strerror(errno));
h = hmapalloc(4096); h = hmapalloc(4096);
lineno = 0; lineno = 0;
while((line = Brdstr(b, '\n', 1)) != nil){ while((line = readline(b, path)) != nil){
lineno++; lineno++;
len = strlen(line); len = Blinelen(b);
if(memchr(line, '\0', len) != nil)
die("NUL in dictionary: %s:%d", path, lineno);
if(len > 0 && line[len-1] == '\r') if(len > 0 && line[len-1] == '\r')
line[--len] = '\0'; line[--len] = '\0';
if(len == 0 || line[0] == ';'){ if(len == 0 || line[0] == ';'){
free(line); free(line);
continue; continue;
} }
tab = strchr(line, '\t'); tab = memchr(line, '\t', len);
if(tab == nil || tab == line || tab >= line + len - 1 || if(tab == nil || tab == line || tab >= line + len - 1 ||
strchr(tab+1, '\t') != nil) memchr(tab+1, '\t', line+len-(tab+1)) != nil)
die("malformed dictionary: %s:%d", path, lineno); die("malformed dictionary: %s:%d", path, lineno);
*tab = '\0'; *tab = '\0';
if(utflen(line) > Maxrunes) if(!sinit(&key, line, tab-line))
die("dictionary key too long: %s:%d", path, lineno); die("invalid or oversized dictionary key: %s:%d",
path, lineno);
for(p = tab+1; p < line+len; p = e+1){ for(p = tab+1; p < line+len; p = e+1){
e = memchr(p, ' ', line+len-p); e = memchr(p, ' ', line+len-p);
if(e == nil) if(e == nil)
e = line+len; e = line+len;
if(utfnlen(p, e-p) > Maxrunes) if(!sinit(&tmp, p, e-p))
die("dictionary candidate too long: %s:%d", path, lineno); die("invalid or oversized dictionary candidate: %s:%d",
path, lineno);
if(e == line+len) if(e == line+len)
break; break;
} }
sinit(&key, line, tab - line);
hmapset(&h, &key, tab+1, len - (tab - line) - 1); hmapset(&h, &key, tab+1, len - (tab - line) - 1);
free(line); free(line);
} }
Bterm(b); if(Bterm(b) < 0)
die("can't close %s: %s", path, strerror(errno));
return h; return h;
} }

3
fn.h
View File

@@ -1,6 +1,6 @@
void die(char*, ...); void die(char*, ...);
void sinit(Str*, char*, int); int sinit(Str*, char*, int);
void sclear(Str*); void sclear(Str*);
void sputr(Str*, Rune); void sputr(Str*, Rune);
void spopr(Str*); void spopr(Str*);
@@ -17,7 +17,6 @@ int mapget(Trie*, Str*, Str*);
Trie* trieopen(char*); Trie* trieopen(char*);
void trieclose(Trie*); void trieclose(Trie*);
char* trieget(Trie*, char*, int, int*);
int trielookup(Trie*, char*, int, char**, int*); int trielookup(Trie*, char*, int, char**, int*);
Lang* getlang(int); Lang* getlang(int);

31
hash.c
View File

@@ -1,3 +1,4 @@
#include <limits.h>
#include "dat.h" #include "dat.h"
#include "fn.h" #include "fn.h"
@@ -27,7 +28,9 @@ hmapalloc(int nbuckets)
if(nbuckets < 1) if(nbuckets < 1)
return nil; return nil;
nsz = Tagsize; nsz = Tagsize;
store = emalloc(sizeof(*h) + nbuckets * nsz); if((ulong)nbuckets > (ULONG_MAX-sizeof(*h))/(ulong)nsz)
return nil;
store = emalloc(sizeof(*h) + (ulong)nbuckets * nsz);
h = store; h = store;
h->nbs = nbuckets; h->nbs = nbuckets;
h->nsz = nsz; h->nsz = nsz;
@@ -56,14 +59,14 @@ hmapget(Hmap *h, Str *key)
if(h == nil || key == nil || key->n < 0 || key->n > Maxrunes) if(h == nil || key == nil || key->n < 0 || key->n > Maxrunes)
return nil; return nil;
v = h->nodes + (hash(key) % h->nbs) * h->nsz; v = h->nodes + (hash(key) % h->nbs) * (ulong)h->nsz;
for(;;){ for(;;){
n = (Hnode*)v; n = (Hnode*)v;
if(n->filled && keycmp(n, key) == 0) if(n->filled && keycmp(n, key) == 0)
return n; return n;
if(n->next == 0) if(n->next == 0)
break; break;
v = h->nodes + n->next * h->nsz; v = h->nodes + (ulong)n->next * h->nsz;
} }
return nil; return nil;
} }
@@ -76,7 +79,7 @@ sdup(Str *s, int *len)
int n; int n;
n = stoutf(s, buf, sizeof(buf)); n = stoutf(s, buf, sizeof(buf));
p = emalloc(n + 1); p = emalloc((ulong)n + 1);
memmove(p, buf, n); memmove(p, buf, n);
p[n] = '\0'; p[n] = '\0';
*len = n; *len = n;
@@ -90,7 +93,7 @@ memdup(const char *src, int n)
if(n == 0) if(n == 0)
return nil; return nil;
p = emalloc(n + 1); p = emalloc((ulong)n + 1);
memmove(p, src, n); memmove(p, src, n);
p[n] = '\0'; p[n] = '\0';
return p; return p;
@@ -105,7 +108,7 @@ hmapfree(Hmap *h)
if(h == nil) if(h == nil)
return; return;
for(i = 0; i < h->len; i++){ for(i = 0; i < h->len; i++){
n = (Hnode*)(h->nodes + i * h->nsz); n = (Hnode*)(h->nodes + (ulong)i * h->nsz);
if(!n->filled) if(!n->filled)
continue; continue;
free(n->key); free(n->key);
@@ -122,7 +125,7 @@ hmapset(Hmap **store, Str *key, const char *val, int vlen)
uchar *v; uchar *v;
Hmap *h; Hmap *h;
int next; int next;
vlong diff; ulong diff;
if(store == nil || *store == nil || key == nil || if(store == nil || *store == nil || key == nil ||
key->n < 0 || key->n > Maxrunes || vlen < 0 || key->n < 0 || key->n > Maxrunes || vlen < 0 ||
@@ -130,7 +133,7 @@ hmapset(Hmap **store, Str *key, const char *val, int vlen)
return; return;
newval = memdup(val, vlen); newval = memdup(val, vlen);
h = *store; h = *store;
v = h->nodes + (hash(key) % h->nbs) * h->nsz; v = h->nodes + (hash(key) % h->nbs) * (ulong)h->nsz;
for(;;){ for(;;){
n = (Hnode*)v; n = (Hnode*)v;
next = n->next; next = n->next;
@@ -140,21 +143,25 @@ hmapset(Hmap **store, Str *key, const char *val, int vlen)
goto replace; goto replace;
if(next == 0) if(next == 0)
break; break;
v = h->nodes + next * h->nsz; v = h->nodes + (ulong)next * h->nsz;
} }
if(h->cap == h->len){ if(h->cap == h->len){
diff = v - h->nodes; diff = v - h->nodes;
if(h->cap > INT_MAX/2 ||
(ulong)h->cap > (ULONG_MAX-sizeof(*h))/(2*(ulong)h->nsz))
die("hash table is too large");
h->cap *= 2; h->cap *= 2;
*store = erealloc(*store, sizeof(*h) + h->cap * h->nsz); *store = erealloc(*store,
sizeof(*h) + (ulong)h->cap * h->nsz);
h = *store; h = *store;
h->nodes = (uchar*)*store + sizeof(*h); h->nodes = (uchar*)*store + sizeof(*h);
v = h->nodes + diff; v = h->nodes + diff;
n = (Hnode*)v; n = (Hnode*)v;
} }
n->next = h->len; n->next = h->len;
memset(h->nodes + h->len * h->nsz, 0, h->nsz); memset(h->nodes + (ulong)h->len * h->nsz, 0, h->nsz);
h->len++; h->len++;
v = h->nodes + n->next * h->nsz; v = h->nodes + (ulong)n->next * h->nsz;
n = (Hnode*)v; n = (Hnode*)v;
replace: replace:
if(n->filled == 0){ if(n->filled == 0){

25
str.c
View File

@@ -1,24 +1,33 @@
#include "dat.h" #include "dat.h"
#include "fn.h" #include "fn.h"
void int
sinit(Str *s, char *src, int n) sinit(Str *s, char *src, int n)
{ {
Str tmp = {0};
Rune r;
int len; int len;
if(s == nil)
return 0;
s->n = 0; s->n = 0;
if(n < 0 || (n > 0 && src == nil)) if(n < 0 || (n > 0 && src == nil))
return; return 0;
while(n > 0 && s->n < Maxrunes){ while(n > 0){
if(tmp.n >= Maxrunes)
return 0;
if(!fullrune(src, n)) if(!fullrune(src, n))
break; return 0;
len = chartorune(&s->r[s->n], src); len = chartorune(&r, src);
if(len > n) if(len > n || (r == Runeerror && len == 1) ||
break; (r >= 0xd800 && r <= 0xdfff))
s->n++; return 0;
tmp.r[tmp.n++] = r;
src += len; src += len;
n -= len; n -= len;
} }
*s = tmp;
return 1;
} }
void void

View File

@@ -406,17 +406,18 @@ static int
maplookup(Trie *t, Str *key, Str *out) maplookup(Trie *t, Str *key, Str *out)
{ {
char buf[Maxutf], *v; char buf[Maxutf], *v;
int klen, vlen; int klen, match, vlen;
if(key->n == 0) if(out != nil)
sclear(out);
if(t == nil || key == nil || key->n == 0)
return 0; return 0;
v = nil;
vlen = 0;
klen = stoutf(key, buf, sizeof(buf)); klen = stoutf(key, buf, sizeof(buf));
if(!trielookup(t, buf, klen, &v, &vlen)) match = trielookup(t, buf, klen, &v, &vlen);
if(match == TrieMiss)
return 0;
if(match == TrieExact && out != nil && !sinit(out, v, vlen))
return 0; return 0;
if(out != nil && v != nil)
sinit(out, v, vlen);
return 1; return 1;
} }
@@ -1043,16 +1044,17 @@ int
mapget(Trie *t, Str *key, Str *out) mapget(Trie *t, Str *key, Str *out)
{ {
char buf[Maxutf], *v; char buf[Maxutf], *v;
int klen, vlen; int klen, match, vlen;
if(key->n == 0) if(out != nil)
sclear(out);
if(key == nil || out == nil || key->n == 0)
return 0; return 0;
klen = stoutf(key, buf, sizeof(buf)); klen = stoutf(key, buf, sizeof(buf));
v = trieget(t, buf, klen, &vlen); match = trielookup(t, buf, klen, &v, &vlen);
if(v == nil) if(match != TrieExact)
return 0; return 0;
sinit(out, v, vlen); return sinit(out, v, vlen);
return 1;
} }
void void

View File

@@ -3,6 +3,7 @@
void void
dictionary_candidates(struct ct *t) dictionary_candidates(struct ct *t)
{ {
static char malformed[] = { 'o', 'k', ' ', (char)0x80 };
char many[512], item[8]; char many[512], item[8];
char *p; char *p;
Dictreq req; Dictreq req;
@@ -53,6 +54,11 @@ dictionary_candidates(struct ct *t)
CT_EQ_INT(t, 0, scmp(&req.pre, &res.key)); CT_EQ_INT(t, 0, scmp(&req.pre, &res.key));
checkstr(t, "first capped candidate", "c00", &res.kouho[0]); checkstr(t, "first capped candidate", "c00", &res.kouho[0]);
checkstr(t, "last capped candidate", "c31", &res.kouho[31]); checkstr(t, "last capped candidate", "c31", &res.kouho[31]);
key = mkstr("malformed");
hmapset(&lang->dict, &key, malformed, sizeof malformed);
req.key = key;
dictlookup(&req, &res);
CT_EQ_INT(t, 0, res.nkouho);
cleanup: cleanup:
hmapfree(lang->dict); hmapfree(lang->dict);
lang->dict = saved; lang->dict = saved;

View File

@@ -3,19 +3,48 @@
void void
str_init_utf8(struct ct *t) str_init_utf8(struct ct *t)
{ {
static char badtail[] = { 'a', (char)0x80 };
static char incomplete[] = { (char)0xea, (char)0xb0 };
static char overlong[] = { (char)0xc0, (char)0xaf };
static char outofrange[] = {
(char)0xf4, (char)0x90, (char)0x80, (char)0x80,
};
static char surrogate[] = {
(char)0xed, (char)0xa0, (char)0x80,
};
static char unexpected[] = { (char)0x80 };
static const struct {
char *name;
char *src;
int n;
int ok;
char *want;
} cases[] = {
{ "canonical Runeerror", "\xef\xbf\xbd", 3, 1, "\xef\xbf\xbd" },
{ "incomplete", incomplete, sizeof incomplete, 0, "" },
{ "unexpected continuation", unexpected, sizeof unexpected, 0, "" },
{ "overlong", overlong, sizeof overlong, 0, "" },
{ "surrogate", surrogate, sizeof surrogate, 0, "" },
{ "out of range", outofrange, sizeof outofrange, 0, "" },
{ "bad tail", badtail, sizeof badtail, 0, "" },
};
char full[Maxrunes+2]; char full[Maxrunes+2];
char incomplete[] = { (char)0xea, (char)0xb0 };
Str s; Str s;
int i;
s = mkstr("A한😀"); s = mkstr("A한😀");
CT_EQ_INT(t, 3, s.n); CT_EQ_INT(t, 3, s.n);
checkstr(t, "round trip", "A한😀", &s); checkstr(t, "round trip", "A한😀", &s);
sinit(&s, incomplete, sizeof incomplete); for(i = 0; i < nelem(cases); i++){
CT_EQ_INT(t, 0, s.n); memset(&s, 0xa5, sizeof s);
if(sinit(&s, cases[i].src, cases[i].n) != cases[i].ok)
CT_ERRORF(t, "%s: wrong validity", cases[i].name);
checkstr(t, cases[i].name, cases[i].want, &s);
}
memset(full, 'a', sizeof full); memset(full, 'a', sizeof full);
full[sizeof full-1] = '\0'; full[sizeof full-1] = '\0';
sinit(&s, full, strlen(full)); CT_CHECK(t, !sinit(&s, full, strlen(full)));
CT_EQ_INT(t, Maxrunes, s.n); CT_EQ_INT(t, 0, s.n);
} }
void void
@@ -89,9 +118,9 @@ str_invalid_and_full_appends(struct ct *t)
Str s; Str s;
int i; int i;
sinit(&s, nil, 1); CT_CHECK(t, !sinit(&s, nil, 1));
CT_EQ_INT(t, 0, s.n); CT_EQ_INT(t, 0, s.n);
sinit(&s, "x", -1); CT_CHECK(t, !sinit(&s, "x", -1));
CT_EQ_INT(t, 0, s.n); CT_EQ_INT(t, 0, s.n);
for(i = 0; i < Maxrunes; i++) for(i = 0; i < Maxrunes; i++)
s.r[i] = 'a'; s.r[i] = 'a';

View File

@@ -3,29 +3,41 @@
void void
trie_exact_prefix_and_duplicate(struct ct *t) trie_exact_prefix_and_duplicate(struct ct *t)
{ {
char *v, *sentinel; static const struct {
char *key;
int klen;
int match;
char *want;
} cases[] = {
{ "a", 1, TrieExact, "alpha" },
{ "dupli", 5, TriePrefix, nil },
{ "duplicate", 9, TrieExact, "second" },
{ "missing", 7, TrieMiss, nil },
{ nil, 1, TrieMiss, nil },
{ "a", -1, TrieMiss, nil },
};
char *v;
Trie *trie; Trie *trie;
int n; int i, match, n;
trie = trieopen("data/trie.map"); trie = trieopen("data/trie.map");
v = trieget(trie, "a", 1, &n); for(i = 0; i < nelem(cases); i++){
if(!CT_CHECK(t, v != nil)) v = "unchanged";
goto cleanup; n = 77;
CT_EQ_INT(t, 5, n); match = trielookup(trie, cases[i].key, cases[i].klen, &v, &n);
CT_EQ_MEM(t, "alpha", v, n); if(match != cases[i].match){
sentinel = "unchanged"; CT_ERRORF(t, "case %d: want match %d, got %d",
v = sentinel; i, cases[i].match, match);
n = 77; continue;
CT_CHECK(t, trielookup(trie, "dupli", 5, &v, &n)); }
CT_EQ_PTR(t, sentinel, v); if(cases[i].want == nil){
CT_EQ_INT(t, 77, n); CT_EQ_PTR(t, nil, v);
v = trieget(trie, "duplicate", 9, &n); CT_EQ_INT(t, 0, n);
if(!CT_CHECK(t, v != nil)) }else{
goto cleanup; CT_EQ_INT(t, strlen(cases[i].want), n);
CT_EQ_INT(t, 6, n); CT_EQ_MEM(t, cases[i].want, v, n);
CT_EQ_MEM(t, "second", v, n); }
CT_EQ_PTR(t, nil, trieget(trie, "missing", 7, &n)); }
cleanup:
trieclose(trie); trieclose(trie);
} }
@@ -37,18 +49,15 @@ trie_optional_outputs_and_invalid_lengths(struct ct *t)
int n; int n;
trie = trieopen("data/trie.map"); trie = trieopen("data/trie.map");
CT_CHECK(t, trieget(trie, "a", 1, nil) != nil); CT_EQ_INT(t, TrieExact, trielookup(trie, "a", 1, &v, nil));
v = nil;
CT_CHECK(t, trielookup(trie, "a", 1, &v, nil));
CT_CHECK(t, v != nil); CT_CHECK(t, v != nil);
n = -1; CT_EQ_INT(t, TrieExact, trielookup(trie, "a", 1, nil, &n));
CT_CHECK(t, trielookup(trie, "a", 1, nil, &n));
CT_EQ_INT(t, 5, n); CT_EQ_INT(t, 5, n);
CT_CHECK(t, trielookup(trie, "dupli", 5, nil, nil)); CT_EQ_INT(t, TriePrefix,
CT_EQ_PTR(t, nil, trieget(trie, nil, 1, &n)); trielookup(trie, "dupli", 5, nil, nil));
CT_EQ_PTR(t, nil, trieget(trie, "a", -1, &n)); CT_EQ_INT(t, TriePrefix, trielookup(trie, nil, 0, &v, &n));
CT_CHECK(t, !trielookup(trie, nil, 1, &v, &n)); CT_EQ_PTR(t, nil, v);
CT_CHECK(t, !trielookup(trie, "a", -1, &v, &n)); CT_EQ_INT(t, 0, n);
trieclose(trie); trieclose(trie);
} }

87
trie.c
View File

@@ -1,14 +1,33 @@
#include <errno.h>
#include <limits.h>
#include "dat.h" #include "dat.h"
#include "fn.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 static int
newnode(Trie *t) newnode(Trie *t)
{ {
int cap;
int i; int i;
if(t->n >= t->cap){ if(t->n >= t->cap){
t->cap *= 2; if(t->cap > INT_MAX/2 ||
t->nodes = erealloc(t->nodes, t->cap * sizeof(Tnode)); (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++; i = t->n++;
memset(&t->nodes[i], 0, sizeof(Tnode)); memset(&t->nodes[i], 0, sizeof(Tnode));
@@ -54,7 +73,7 @@ insert(Trie *t, char *key, int klen, char *val, int vlen)
ni = ci; ni = ci;
} }
free(t->nodes[ni].val); free(t->nodes[ni].val);
t->nodes[ni].val = emalloc(vlen + 1); t->nodes[ni].val = emalloc((ulong)vlen + 1);
memmove(t->nodes[ni].val, val, vlen); memmove(t->nodes[ni].val, val, vlen);
t->nodes[ni].val[vlen] = '\0'; t->nodes[ni].val[vlen] = '\0';
t->nodes[ni].vlen = vlen; t->nodes[ni].vlen = vlen;
@@ -66,41 +85,45 @@ trieopen(char *path)
Trie *t; Trie *t;
Biobuf *b; Biobuf *b;
char *line, *tab, *key, *val; char *line, *tab, *key, *val;
Str s;
int klen, vlen; int klen, vlen;
b = Bopen(path, OREAD); b = Bopen(path, OREAD);
if(b == nil) if(b == nil)
die("can't open: %s", path); die("can't open %s: %s", path, strerror(errno));
t = emalloc(sizeof(*t)); t = emalloc(sizeof(*t));
t->cap = 1024; t->cap = 1024;
t->nodes = emalloc(t->cap * sizeof(Tnode)); t->nodes = emalloc((ulong)t->cap * sizeof(Tnode));
t->n = 0; t->n = 0;
t->root = newnode(t); t->root = newnode(t);
while((line = Brdstr(b, '\n', 1)) != nil){ while((line = readline(b, path)) != nil){
vlen = strlen(line); vlen = Blinelen(b);
if(memchr(line, '\0', vlen) != nil)
die("NUL in map: %s", path);
if(vlen > 0 && line[vlen-1] == '\r') if(vlen > 0 && line[vlen-1] == '\r')
line[--vlen] = '\0'; line[--vlen] = '\0';
if(line[0] == '\0' || line[0] == ';'){ if(line[0] == '\0' || line[0] == ';'){
free(line); free(line);
continue; continue;
} }
tab = strchr(line, '\t'); tab = memchr(line, '\t', vlen);
if(tab == nil || tab == line || tab[1] == '\0' || if(tab == nil || tab == line || tab == line+vlen-1 ||
strchr(tab+1, '\t') != nil) memchr(tab+1, '\t', line+vlen-(tab+1)) != nil)
die("malformed map: %s", path); die("malformed map: %s", path);
*tab = '\0'; *tab = '\0';
key = line; key = line;
klen = tab - line; klen = tab - line;
if(utflen(key) > Maxrunes) if(!sinit(&s, key, klen))
die("map key too long: %s", path); die("invalid or oversized map key: %s", path);
val = tab + 1; val = tab + 1;
vlen = strlen(val); vlen = line + vlen - val;
if(utflen(val) > Maxrunes) if(!sinit(&s, val, vlen))
die("map value too long: %s", path); die("invalid or oversized map value: %s", path);
insert(t, key, klen, val, vlen); insert(t, key, klen, val, vlen);
free(line); free(line);
} }
Bterm(b); if(Bterm(b) < 0)
die("can't close %s: %s", path, strerror(errno));
return t; return t;
} }
@@ -117,46 +140,30 @@ trieclose(Trie *t)
free(t); free(t);
} }
char*
trieget(Trie *t, char *key, int klen, int *vlen)
{
int ni;
int i;
if(t == nil || klen < 0 || (klen > 0 && key == nil))
return nil;
ni = t->root;
for(i = 0; i < klen; i++){
ni = find(t, ni, key[i]);
if(ni < 0)
return nil;
}
if(t->nodes[ni].val == nil)
return nil;
if(vlen != nil)
*vlen = t->nodes[ni].vlen;
return t->nodes[ni].val;
}
int int
trielookup(Trie *t, char *key, int klen, char **val, int *vlen) trielookup(Trie *t, char *key, int klen, char **val, int *vlen)
{ {
int ni; int ni;
int i; int i;
if(val != nil)
*val = nil;
if(vlen != nil)
*vlen = 0;
if(t == nil || klen < 0 || (klen > 0 && key == nil)) if(t == nil || klen < 0 || (klen > 0 && key == nil))
return 0; return TrieMiss;
ni = t->root; ni = t->root;
for(i = 0; i < klen; i++){ for(i = 0; i < klen; i++){
ni = find(t, ni, key[i]); ni = find(t, ni, key[i]);
if(ni < 0) if(ni < 0)
return 0; return TrieMiss;
} }
if(t->nodes[ni].val != nil){ if(t->nodes[ni].val != nil){
if(val != nil) if(val != nil)
*val = t->nodes[ni].val; *val = t->nodes[ni].val;
if(vlen != nil) if(vlen != nil)
*vlen = t->nodes[ni].vlen; *vlen = t->nodes[ni].vlen;
return TrieExact;
} }
return 1; return TriePrefix;
} }