Use a trie instead of a linear list of sequence strings in the terminfo driver - nicer lookup properties
This commit is contained in:
parent
04e1926df6
commit
754214c200
195
driver-ti.c
195
driver-ti.c
|
@ -9,23 +9,104 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
struct ti_keyinfo {
|
/* To be efficient at lookups, we store the byte sequence => keyinfo mapping
|
||||||
const char *seq;
|
* in a trie. This avoids a slow linear search though a flat list of sequences
|
||||||
size_t seqlen; // cached strlen of seq since we'll use it lots
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TYPE_KEY,
|
||||||
|
TYPE_ARR
|
||||||
|
} trie_nodetype;
|
||||||
|
|
||||||
|
struct trie_node {
|
||||||
|
trie_nodetype type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct trie_node_key {
|
||||||
|
trie_nodetype type;
|
||||||
struct keyinfo key;
|
struct keyinfo key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct trie_node_arr {
|
||||||
|
trie_nodetype type;
|
||||||
|
struct trie_node *arr[256];
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
termkey_t *tk;
|
termkey_t *tk;
|
||||||
|
|
||||||
int nseqs;
|
struct trie_node *root;
|
||||||
int alloced_seqs;
|
|
||||||
struct ti_keyinfo *seqs;
|
|
||||||
} termkey_ti;
|
} termkey_ti;
|
||||||
|
|
||||||
static int funcname2keysym(const char *funcname, termkey_type *typep, termkey_keysym *symp, int *modmask, int *modsetp);
|
static int funcname2keysym(const char *funcname, termkey_type *typep, termkey_keysym *symp, int *modmask, int *modsetp);
|
||||||
static int register_seq(termkey_ti *ti, const char *seq, termkey_type type, termkey_keysym sym, int modmask, int modset);
|
static int register_seq(termkey_ti *ti, const char *seq, termkey_type type, termkey_keysym sym, int modmask, int modset);
|
||||||
|
|
||||||
|
static struct trie_node *new_node_key(termkey_type type, termkey_keysym sym, int modmask, int modset)
|
||||||
|
{
|
||||||
|
struct trie_node_key *n = malloc(sizeof(struct trie_node_key));
|
||||||
|
if(!n)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n->type = TYPE_KEY;
|
||||||
|
|
||||||
|
n->key.type = type;
|
||||||
|
n->key.sym = sym;
|
||||||
|
n->key.modifier_mask = modmask;
|
||||||
|
n->key.modifier_set = modset;
|
||||||
|
|
||||||
|
return (struct trie_node*)n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct trie_node *new_node_arr(void)
|
||||||
|
{
|
||||||
|
struct trie_node_arr *n = malloc(sizeof(struct trie_node_arr));
|
||||||
|
if(!n)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n->type = TYPE_ARR;
|
||||||
|
|
||||||
|
unsigned char b;
|
||||||
|
for(b = 0; b < 255; b++)
|
||||||
|
n->arr[b] = NULL;
|
||||||
|
|
||||||
|
return (struct trie_node*)n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct trie_node *lookup_next(struct trie_node *n, unsigned char b)
|
||||||
|
{
|
||||||
|
switch(n->type) {
|
||||||
|
case TYPE_KEY:
|
||||||
|
fprintf(stderr, "ABORT: lookup_next within a TYPE_KEY node\n");
|
||||||
|
abort();
|
||||||
|
case TYPE_ARR:
|
||||||
|
{
|
||||||
|
struct trie_node_arr *nar = (struct trie_node_arr*)n;
|
||||||
|
return nar->arr[b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL; // Never reached but keeps compiler happy
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_trie(struct trie_node *n)
|
||||||
|
{
|
||||||
|
switch(n->type) {
|
||||||
|
case TYPE_KEY:
|
||||||
|
break;
|
||||||
|
case TYPE_ARR:
|
||||||
|
{
|
||||||
|
struct trie_node_arr *nar = (struct trie_node_arr*)n;
|
||||||
|
unsigned char b;
|
||||||
|
for(b = 0; b < 255; b++)
|
||||||
|
if(nar->arr[b])
|
||||||
|
free_trie(nar->arr[b]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
|
||||||
static void *new_driver(termkey_t *tk, const char *term)
|
static void *new_driver(termkey_t *tk, const char *term)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -39,11 +120,8 @@ static void *new_driver(termkey_t *tk, const char *term)
|
||||||
|
|
||||||
ti->tk = tk;
|
ti->tk = tk;
|
||||||
|
|
||||||
ti->alloced_seqs = 32; // We'll allocate more space if we need
|
ti->root = new_node_arr();
|
||||||
ti->nseqs = 0;
|
if(!ti->root)
|
||||||
|
|
||||||
ti->seqs = malloc(ti->alloced_seqs * sizeof(ti->seqs[0]));
|
|
||||||
if(!ti->seqs)
|
|
||||||
goto abort_free_ti;
|
goto abort_free_ti;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
@ -67,13 +145,13 @@ static void *new_driver(termkey_t *tk, const char *term)
|
||||||
|
|
||||||
if(sym != TERMKEY_SYM_NONE)
|
if(sym != TERMKEY_SYM_NONE)
|
||||||
if(!register_seq(ti, value, type, sym, mask, set))
|
if(!register_seq(ti, value, type, sym, mask, set))
|
||||||
goto abort_free_seqs;
|
goto abort_free_trie;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ti;
|
return ti;
|
||||||
|
|
||||||
abort_free_seqs:
|
abort_free_trie:
|
||||||
free(ti->seqs);
|
free_trie(ti->root);
|
||||||
|
|
||||||
abort_free_ti:
|
abort_free_ti:
|
||||||
free(ti);
|
free(ti);
|
||||||
|
@ -104,7 +182,7 @@ static void free_driver(void *info)
|
||||||
{
|
{
|
||||||
termkey_ti *ti = info;
|
termkey_ti *ti = info;
|
||||||
|
|
||||||
free(ti->seqs);
|
free_trie(ti->root);
|
||||||
|
|
||||||
free(ti);
|
free(ti);
|
||||||
}
|
}
|
||||||
|
@ -118,31 +196,30 @@ static termkey_result getkey(termkey_t *tk, void *info, termkey_key *key, int fo
|
||||||
if(tk->buffcount == 0)
|
if(tk->buffcount == 0)
|
||||||
return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE;
|
return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE;
|
||||||
|
|
||||||
// Now we're sure at least 1 byte is valid
|
struct trie_node *p = ti->root;
|
||||||
unsigned char b0 = CHARAT(0);
|
|
||||||
|
|
||||||
int i;
|
int pos = 0;
|
||||||
for(i = 0; i < ti->nseqs; i++) {
|
while(pos < tk->buffcount) {
|
||||||
struct ti_keyinfo *s = &(ti->seqs[i]);
|
p = lookup_next(p, CHARAT(pos));
|
||||||
|
if(!p)
|
||||||
|
break;
|
||||||
|
|
||||||
if(s->seq[0] != b0)
|
pos++;
|
||||||
continue;
|
|
||||||
|
|
||||||
if(tk->buffcount >= s->seqlen) {
|
if(p->type == TYPE_KEY) {
|
||||||
if(strncmp(s->seq, (const char*)tk->buffer + tk->buffstart, s->seqlen) == 0) {
|
struct trie_node_key *nk = (struct trie_node_key*)p;
|
||||||
key->type = s->key.type;
|
key->type = nk->key.type;
|
||||||
key->code.sym = s->key.sym;
|
key->code.sym = nk->key.sym;
|
||||||
key->modifiers = s->key.modifier_set;
|
key->modifiers = nk->key.modifier_set;
|
||||||
(*tk->method.eat_bytes)(tk, s->seqlen);
|
(*tk->method.eat_bytes)(tk, pos);
|
||||||
return TERMKEY_RES_KEY;
|
return TERMKEY_RES_KEY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!force) {
|
|
||||||
// Maybe we'd get a partial match
|
// If p is not NULL then we hadn't walked off the end yet, so we have a
|
||||||
if(strncmp(s->seq, (const char*)tk->buffer + tk->buffstart, tk->buffcount) == 0)
|
// partial match
|
||||||
|
if(p && !force)
|
||||||
return TERMKEY_RES_AGAIN;
|
return TERMKEY_RES_AGAIN;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TERMKEY_RES_NONE;
|
return TERMKEY_RES_NONE;
|
||||||
}
|
}
|
||||||
|
@ -246,21 +323,47 @@ static int funcname2keysym(const char *funcname, termkey_type *typep, termkey_ke
|
||||||
|
|
||||||
static int register_seq(termkey_ti *ti, const char *seq, termkey_type type, termkey_keysym sym, int modmask, int modset)
|
static int register_seq(termkey_ti *ti, const char *seq, termkey_type type, termkey_keysym sym, int modmask, int modset)
|
||||||
{
|
{
|
||||||
if(ti->nseqs == ti->alloced_seqs) {
|
int pos = 0;
|
||||||
ti->alloced_seqs *= 2;
|
struct trie_node *p = ti->root;
|
||||||
void *newseqs = realloc(ti->seqs, ti->alloced_seqs * sizeof(ti->seqs[9]));
|
|
||||||
if(!newseqs)
|
// Unsigned because we'll be using it as an array subscript
|
||||||
return 0;
|
unsigned char b;
|
||||||
ti->seqs = newseqs;
|
|
||||||
|
while((b = seq[pos])) {
|
||||||
|
struct trie_node *next = lookup_next(p, b);
|
||||||
|
if(!next)
|
||||||
|
break;
|
||||||
|
p = next;
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i = ti->nseqs++;
|
while((b = seq[pos])) {
|
||||||
ti->seqs[i].seq = seq;
|
struct trie_node *next;
|
||||||
ti->seqs[i].seqlen = strlen(seq);
|
if(seq[pos+1])
|
||||||
ti->seqs[i].key.type = type;
|
// Intermediate node
|
||||||
ti->seqs[i].key.sym = sym;
|
next = new_node_arr();
|
||||||
ti->seqs[i].key.modifier_mask = modmask;
|
else
|
||||||
ti->seqs[i].key.modifier_set = modset;
|
// Final key node
|
||||||
|
next = new_node_key(type, sym, modmask, modset);
|
||||||
|
|
||||||
|
if(!next)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch(p->type) {
|
||||||
|
case TYPE_ARR:
|
||||||
|
{
|
||||||
|
struct trie_node_arr *nar = (struct trie_node_arr*)p;
|
||||||
|
nar->arr[b] = next;
|
||||||
|
p = next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TYPE_KEY:
|
||||||
|
fprintf(stderr, "ASSERT FAIL: Tried to insert child node in TYPE_KEY\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue