trie: one node per rune, children in rune order, and the last path kept
The trie walked a key byte by byte, so a Hangul syllable cost three nodes and every lookup first spelled its key back into UTF-8. It walks runes now: a third of the nodes for Korean data, and no buffer in the lookup. Children are kept in rune order, so both the search and the insert stop early, and a trie remembers the path of the last key put, which a file sorted by key — hanja.dict is one — walks straight down.
This commit is contained in:
58
trie.c
58
trie.c
@@ -3,9 +3,9 @@
|
||||
#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.
|
||||
* A trie over the runes of a key. Maps need prefix matches while
|
||||
* composing; dictionaries need exact matches; both are small enough for
|
||||
* one structure. Node 0 is the root.
|
||||
*/
|
||||
|
||||
static int
|
||||
@@ -24,32 +24,37 @@ newnode(Trie *t)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* The children of a node are kept in rune order, so both end early. */
|
||||
static int
|
||||
find(Trie *t, int ni, char c)
|
||||
find(Trie *t, int ni, Rune c)
|
||||
{
|
||||
int pi;
|
||||
|
||||
for(pi = t->nodes[ni].child; pi >= 0; pi = t->nodes[pi].sibling)
|
||||
for(pi = t->nodes[ni].child; pi >= 0; pi = t->nodes[pi].sibling){
|
||||
if(t->nodes[pi].c == c)
|
||||
return pi;
|
||||
if(t->nodes[pi].c > c)
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Appends, so that a walk of the children keeps the file's order. */
|
||||
static int
|
||||
add(Trie *t, int ni, char c)
|
||||
add(Trie *t, int ni, Rune c)
|
||||
{
|
||||
int last, pi;
|
||||
int next, pi, prev;
|
||||
|
||||
last = -1;
|
||||
for(pi = t->nodes[ni].child; pi >= 0; pi = t->nodes[pi].sibling)
|
||||
last = pi;
|
||||
prev = -1;
|
||||
for(next = t->nodes[ni].child; next >= 0 && t->nodes[next].c < c;
|
||||
next = t->nodes[next].sibling)
|
||||
prev = next;
|
||||
pi = newnode(t);
|
||||
t->nodes[pi].c = c;
|
||||
if(last < 0)
|
||||
t->nodes[pi].sibling = next;
|
||||
if(prev < 0)
|
||||
t->nodes[ni].child = pi;
|
||||
else
|
||||
t->nodes[last].sibling = pi;
|
||||
t->nodes[prev].sibling = pi;
|
||||
return pi;
|
||||
}
|
||||
|
||||
@@ -69,15 +74,26 @@ trienew(void)
|
||||
void
|
||||
trieput(Trie *t, char *key, int klen, char *val, int vlen)
|
||||
{
|
||||
int ni, ci, i;
|
||||
Str k;
|
||||
int ci, i, ni, same;
|
||||
|
||||
if(!sinit(&k, key, klen))
|
||||
return;
|
||||
ni = 0;
|
||||
for(i = 0; i < klen; i++){
|
||||
ci = find(t, ni, key[i]);
|
||||
same = 1;
|
||||
for(i = 0; i < k.n; i++){
|
||||
if(same && i < t->last.n && t->last.r[i] == k.r[i]){
|
||||
ni = t->path[i];
|
||||
continue;
|
||||
}
|
||||
same = 0;
|
||||
ci = find(t, ni, k.r[i]);
|
||||
if(ci < 0)
|
||||
ci = add(t, ni, key[i]);
|
||||
ci = add(t, ni, k.r[i]);
|
||||
ni = ci;
|
||||
t->path[i] = ni;
|
||||
}
|
||||
t->last = k;
|
||||
free(t->nodes[ni].val);
|
||||
t->nodes[ni].val = emalloc(vlen + 1);
|
||||
memmove(t->nodes[ni].val, val, vlen);
|
||||
@@ -163,15 +179,13 @@ trieclose(Trie *t)
|
||||
int
|
||||
trienode(Trie *t, Str *key)
|
||||
{
|
||||
char buf[Maxutf];
|
||||
int i, klen, ni;
|
||||
int i, ni;
|
||||
|
||||
if(t == nil)
|
||||
return -1;
|
||||
klen = stoutf(key, buf, sizeof buf);
|
||||
ni = 0;
|
||||
for(i = 0; i < klen && ni >= 0; i++)
|
||||
ni = find(t, ni, buf[i]);
|
||||
for(i = 0; i < key->n && ni >= 0; i++)
|
||||
ni = find(t, ni, key->r[i]);
|
||||
return ni;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user