The hash map served a single exact-match lookup that the trie already answers, at the price of a second container, a second file loader with its own drift, and a Str-to-UTF-8 conversion on every chain probe. The files are small (kanji.dict is 7.5k lines), so the trie holds both. trieopen validates keys and each space-separated value word against Str and reports path:line; trielookup takes the Str every caller holds and treats a nil trie as an unloaded map; trienew/trieput exist for tests. The overflow guards on growth, Trie.root (always 0) and the per-language init loop written twice are gone.
133 lines
2.9 KiB
C
133 lines
2.9 KiB
C
#include "test.h"
|
|
|
|
void
|
|
trie_exact_prefix_and_duplicate(struct ct *t)
|
|
{
|
|
static const struct {
|
|
char *key;
|
|
int match;
|
|
char *want;
|
|
} cases[] = {
|
|
{ "a", TrieExact, "alpha" },
|
|
{ "ab", TrieExact, "beta" },
|
|
{ "한", TrieExact, "값" },
|
|
{ "dupli", TriePrefix, nil },
|
|
{ "duplicate", TrieExact, "second" },
|
|
{ "missing", TrieMiss, nil },
|
|
{ "", TriePrefix, nil },
|
|
};
|
|
char *v;
|
|
Trie *trie;
|
|
Str key;
|
|
int i, match, n;
|
|
|
|
trie = trieopen("data/trie.map");
|
|
for(i = 0; i < nelem(cases); i++){
|
|
key = mkstr(cases[i].key);
|
|
match = trielookup(trie, &key, &v, &n);
|
|
if(match != cases[i].match){
|
|
CT_ERRORF(t, "case %d: want match %d, got %d",
|
|
i, cases[i].match, match);
|
|
continue;
|
|
}
|
|
if(cases[i].want == nil){
|
|
CT_EQ_PTR(t, nil, v);
|
|
CT_EQ_INT(t, 0, n);
|
|
}else{
|
|
CT_EQ_INT(t, strlen(cases[i].want), n);
|
|
CT_EQ_MEM(t, cases[i].want, v, n);
|
|
}
|
|
}
|
|
trieclose(trie);
|
|
}
|
|
|
|
void
|
|
trie_put_and_unloaded(struct ct *t)
|
|
{
|
|
char *v;
|
|
Trie *trie;
|
|
Str key;
|
|
int n;
|
|
|
|
key = mkstr("k");
|
|
CT_EQ_INT(t, TrieMiss, trielookup(nil, &key, &v, &n));
|
|
trie = trienew();
|
|
CT_EQ_INT(t, TrieMiss, trielookup(trie, &key, &v, &n));
|
|
trieput(trie, "k", 1, "one two", 7);
|
|
trieput(trie, "ka", 2, "", 0);
|
|
if(CT_EQ_INT(t, TrieExact, trielookup(trie, &key, &v, &n)))
|
|
CT_EQ_MEM(t, "one two", v, 7);
|
|
key = mkstr("ka");
|
|
if(CT_EQ_INT(t, TrieExact, trielookup(trie, &key, &v, &n)))
|
|
CT_EQ_INT(t, 0, n);
|
|
trieput(trie, "k", 1, "three", 5);
|
|
key = mkstr("k");
|
|
if(CT_EQ_INT(t, TrieExact, trielookup(trie, &key, &v, &n)))
|
|
CT_EQ_MEM(t, "three", v, 5);
|
|
trieclose(trie);
|
|
}
|
|
|
|
void
|
|
production_maps_load(struct ct *t)
|
|
{
|
|
char path[256];
|
|
Trie *map;
|
|
int i, loaded, registered;
|
|
|
|
loaded = registered = 0;
|
|
for(i = 0; i < nlang; i++){
|
|
if(langs[i].mapname == nil)
|
|
continue;
|
|
registered++;
|
|
snprint(path, sizeof path, "../map/%s.map", langs[i].mapname);
|
|
map = trieopen(path);
|
|
if(!CT_CHECK(t, map != nil))
|
|
continue;
|
|
trieclose(map);
|
|
loaded++;
|
|
}
|
|
CT_CHECK(t, registered > 0);
|
|
CT_EQ_INT(t, registered, loaded);
|
|
}
|
|
|
|
void
|
|
transmap_states(struct ct *t)
|
|
{
|
|
static const struct {
|
|
char *pre;
|
|
Rune key;
|
|
int eat;
|
|
char *emit;
|
|
char *next;
|
|
char *mapped;
|
|
} cases[] = {
|
|
{ "", 'k', 1, "", "k", "" },
|
|
{ "k", 'a', 1, "", "ka", "か" },
|
|
{ "ka", 's', 1, "か", "s", "" },
|
|
{ "ka", 'q', 0, "か", "", "" },
|
|
{ "k", 'q', 0, "k", "", "" },
|
|
};
|
|
Emit e;
|
|
Im state;
|
|
Trie *fixture, *saved;
|
|
int i;
|
|
|
|
fixture = trieopen("data/hira.map");
|
|
memset(&state, 0, sizeof state);
|
|
state.l = getlang(LangJP);
|
|
saved = state.l->map;
|
|
state.l->map = fixture;
|
|
for(i = 0; i < nelem(cases); i++){
|
|
state.pre = mkstr(cases[i].pre);
|
|
e = transmap(&state, cases[i].key);
|
|
if(e.eat != cases[i].eat)
|
|
CT_ERRORF(t, "case %d: want eat %d, got %d",
|
|
i, cases[i].eat, e.eat);
|
|
checkstr(t, "emit", cases[i].emit, &e.s);
|
|
checkstr(t, "next", cases[i].next, &e.next);
|
|
checkstr(t, "mapped", cases[i].mapped, &e.dict);
|
|
}
|
|
trieclose(fixture);
|
|
state.l->map = saved;
|
|
}
|