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:
5
dat.h
5
dat.h
@@ -74,7 +74,7 @@ struct Tnode
|
|||||||
int vlen;
|
int vlen;
|
||||||
int child;
|
int child;
|
||||||
int sibling;
|
int sibling;
|
||||||
char c;
|
Rune c;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Trie Trie;
|
typedef struct Trie Trie;
|
||||||
@@ -83,6 +83,9 @@ struct Trie
|
|||||||
Tnode *nodes;
|
Tnode *nodes;
|
||||||
int n;
|
int n;
|
||||||
int cap;
|
int cap;
|
||||||
|
/* the last key put and its path, so a sorted file walks it once */
|
||||||
|
Str last;
|
||||||
|
int path[Maxrunes];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|||||||
2
dict.c
2
dict.c
@@ -24,7 +24,7 @@ words(Tnode *nd, Str *out, int n, int max)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The entries at and below a node, in the dictionary's order. */
|
/* The entries at and below a node, the shortest keys first. */
|
||||||
static int
|
static int
|
||||||
below(Trie *t, int ni, Str *out, int n, int max)
|
below(Trie *t, int ni, Str *out, int n, int max)
|
||||||
{
|
{
|
||||||
|
|||||||
62
trie.c
62
trie.c
@@ -3,9 +3,9 @@
|
|||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A byte trie over UTF-8 keys. Maps need prefix matches while composing;
|
* A trie over the runes of a key. Maps need prefix matches while
|
||||||
* dictionaries need exact matches; both are small enough for one structure.
|
* composing; dictionaries need exact matches; both are small enough for
|
||||||
* Node 0 is the root.
|
* one structure. Node 0 is the root.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -24,32 +24,37 @@ newnode(Trie *t)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The children of a node are kept in rune order, so both end early. */
|
||||||
static int
|
static int
|
||||||
find(Trie *t, int ni, char c)
|
find(Trie *t, int ni, Rune c)
|
||||||
{
|
{
|
||||||
int pi;
|
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)
|
if(t->nodes[pi].c == c)
|
||||||
return pi;
|
return pi;
|
||||||
|
if(t->nodes[pi].c > c)
|
||||||
|
break;
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Appends, so that a walk of the children keeps the file's order. */
|
|
||||||
static int
|
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;
|
prev = -1;
|
||||||
for(pi = t->nodes[ni].child; pi >= 0; pi = t->nodes[pi].sibling)
|
for(next = t->nodes[ni].child; next >= 0 && t->nodes[next].c < c;
|
||||||
last = pi;
|
next = t->nodes[next].sibling)
|
||||||
|
prev = next;
|
||||||
pi = newnode(t);
|
pi = newnode(t);
|
||||||
t->nodes[pi].c = c;
|
t->nodes[pi].c = c;
|
||||||
if(last < 0)
|
t->nodes[pi].sibling = next;
|
||||||
|
if(prev < 0)
|
||||||
t->nodes[ni].child = pi;
|
t->nodes[ni].child = pi;
|
||||||
else
|
else
|
||||||
t->nodes[last].sibling = pi;
|
t->nodes[prev].sibling = pi;
|
||||||
return pi;
|
return pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,15 +74,26 @@ trienew(void)
|
|||||||
void
|
void
|
||||||
trieput(Trie *t, char *key, int klen, char *val, int vlen)
|
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;
|
ni = 0;
|
||||||
for(i = 0; i < klen; i++){
|
same = 1;
|
||||||
ci = find(t, ni, key[i]);
|
for(i = 0; i < k.n; i++){
|
||||||
if(ci < 0)
|
if(same && i < t->last.n && t->last.r[i] == k.r[i]){
|
||||||
ci = add(t, ni, key[i]);
|
ni = t->path[i];
|
||||||
ni = ci;
|
continue;
|
||||||
}
|
}
|
||||||
|
same = 0;
|
||||||
|
ci = find(t, ni, k.r[i]);
|
||||||
|
if(ci < 0)
|
||||||
|
ci = add(t, ni, k.r[i]);
|
||||||
|
ni = ci;
|
||||||
|
t->path[i] = ni;
|
||||||
|
}
|
||||||
|
t->last = k;
|
||||||
free(t->nodes[ni].val);
|
free(t->nodes[ni].val);
|
||||||
t->nodes[ni].val = emalloc(vlen + 1);
|
t->nodes[ni].val = emalloc(vlen + 1);
|
||||||
memmove(t->nodes[ni].val, val, vlen);
|
memmove(t->nodes[ni].val, val, vlen);
|
||||||
@@ -163,15 +179,13 @@ trieclose(Trie *t)
|
|||||||
int
|
int
|
||||||
trienode(Trie *t, Str *key)
|
trienode(Trie *t, Str *key)
|
||||||
{
|
{
|
||||||
char buf[Maxutf];
|
int i, ni;
|
||||||
int i, klen, ni;
|
|
||||||
|
|
||||||
if(t == nil)
|
if(t == nil)
|
||||||
return -1;
|
return -1;
|
||||||
klen = stoutf(key, buf, sizeof buf);
|
|
||||||
ni = 0;
|
ni = 0;
|
||||||
for(i = 0; i < klen && ni >= 0; i++)
|
for(i = 0; i < key->n && ni >= 0; i++)
|
||||||
ni = find(t, ni, buf[i]);
|
ni = find(t, ni, key->r[i]);
|
||||||
return ni;
|
return ni;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user