build a really prefix tree

This commit is contained in:
c9s 2014-05-15 22:57:13 +08:00
parent 01a0b0da01
commit cd3e98465b
5 changed files with 184 additions and 39 deletions

View file

@ -29,7 +29,7 @@ void rnode_free(rnode * tree);
void redge_free(redge * edge); void redge_free(redge * edge);
bool rnode_add_child(rnode * n, char * pat , rnode *child); redge * rnode_add_child(rnode * n, char * pat , rnode *child);
redge * rnode_find_edge(rnode * n, char * pat); redge * rnode_find_edge(rnode * n, char * pat);

View file

@ -7,6 +7,11 @@
#ifndef STR_H #ifndef STR_H
#define STR_H #define STR_H
int strndiff(char * d1, char * d2, unsigned int n);
int strdiff(char * d1, char * d2);
char * slug_to_pcre(char * slug, char sep); char * slug_to_pcre(char * slug, char sep);
char * ltrim_slash(char* str); char * ltrim_slash(char* str);

View file

@ -17,9 +17,9 @@
#include "token.h" #include "token.h"
struct _rnode { struct _rnode {
redge ** children; redge ** edges;
int children_len; int edge_len;
int children_cap; int edge_cap;
/* the combined regexp pattern string from pattern_tokens */ /* the combined regexp pattern string from pattern_tokens */
char * combined_pattern; char * combined_pattern;
@ -45,9 +45,9 @@ struct _redge {
rnode * rnode_create(int cap) { rnode * rnode_create(int cap) {
rnode * n = (rnode*) malloc( sizeof(rnode) ); rnode * n = (rnode*) malloc( sizeof(rnode) );
n->children = (redge**) malloc( sizeof(redge*) * 10 ); n->edges = (redge**) malloc( sizeof(redge*) * 10 );
n->children_len = 0; n->edge_len = 0;
n->children_cap = 10; n->edge_cap = 10;
n->endpoint = 0; n->endpoint = 0;
n->combined_pattern = NULL; n->combined_pattern = NULL;
// n->edge_patterns = token_array_create(10); // n->edge_patterns = token_array_create(10);
@ -55,10 +55,12 @@ rnode * rnode_create(int cap) {
} }
void rnode_free(rnode * tree) { void rnode_free(rnode * tree) {
for (int i = 0 ; i < tree->children_len ; i++ ) { for (int i = 0 ; i < tree->edge_len ; i++ ) {
redge_free(tree->children[ i ]); if (tree->edges[i]) {
redge_free(tree->edges[ i ]);
}
} }
free(tree->children); free(tree->edges);
// token_array_free(tree->edge_patterns); // token_array_free(tree->edge_patterns);
free(tree); free(tree);
tree = NULL; tree = NULL;
@ -67,38 +69,38 @@ void rnode_free(rnode * tree) {
/* parent node, edge pattern, child */ /* parent node, edge pattern, child */
bool rnode_add_child(rnode * n, char * pat , rnode *child) { redge * rnode_add_child(rnode * n, char * pat , rnode *child) {
// find the same sub-pattern, if it does not exist, create one // find the same sub-pattern, if it does not exist, create one
redge * e; redge * e;
e = rnode_find_edge(n, pat); e = rnode_find_edge(n, pat);
if (e) { if (e) {
return FALSE; return e;
} }
e = redge_create( pat, strlen(pat), child); e = redge_create( pat, strlen(pat), child);
rnode_append_edge(n, e); rnode_append_edge(n, e);
// token_array_append(n->edge_patterns, pat); // token_array_append(n->edge_patterns, pat);
// assert( token_array_len(n->edge_patterns) == n->children_len ); // assert( token_array_len(n->edge_patterns) == n->edge_len );
return TRUE; return e;
} }
void rnode_append_edge(rnode *n, redge *e) { void rnode_append_edge(rnode *n, redge *e) {
if (n->children_len >= n->children_cap) { if (n->edge_len >= n->edge_cap) {
n->children_cap *= 2; n->edge_cap *= 2;
n->children = realloc(n->children, sizeof(redge) * n->children_cap); n->edges = realloc(n->edges, sizeof(redge) * n->edge_cap);
} }
n->children[ n->children_len++ ] = e; n->edges[ n->edge_len++ ] = e;
} }
redge * rnode_find_edge(rnode * n, char * pat) { redge * rnode_find_edge(rnode * n, char * pat) {
redge * e; redge * e;
for (int i = 0 ; i < n->children_len ; i++ ) { for (int i = 0 ; i < n->edge_len ; i++ ) {
e = n->children[i]; e = n->edges[i];
if ( strcmp(e->pattern, pat) == 0 ) { if ( strcmp(e->pattern, pat) == 0 ) {
return e; return e;
} }
@ -123,8 +125,8 @@ void rnode_combine_patterns(rnode * n) {
p = cpat; p = cpat;
redge *e = NULL; redge *e = NULL;
for ( int i = 0 ; i < n->children_len ; i++ ) { for ( int i = 0 ; i < n->edge_len ; i++ ) {
e = n->children[i]; e = n->edges[i];
strncat(p++,"(", 1); strncat(p++,"(", 1);
strncat(p, e->pattern, e->pattern_len); strncat(p, e->pattern, e->pattern_len);
@ -132,7 +134,7 @@ void rnode_combine_patterns(rnode * n) {
strncat(p++,")", 1); strncat(p++,")", 1);
if ( i + 1 < n->children_len ) { if ( i + 1 < n->edge_len ) {
strncat(p++,"|",1); strncat(p++,"|",1);
} }
} }
@ -161,7 +163,6 @@ rnode * rnode_lookup(rnode * tree, char * path, int path_len) {
rnode * rnode_insert_tokens(rnode * tree, token_array * tokens) { rnode * rnode_insert_tokens(rnode * tree, token_array * tokens) {
rnode * n = tree; rnode * n = tree;
redge * e = NULL; redge * e = NULL;
@ -187,16 +188,121 @@ rnode * rnode_insert_route(rnode *tree, char *route)
rnode * rnode_insert_routel(rnode *tree, char *route, int route_len) rnode * rnode_insert_routel(rnode *tree, char *route, int route_len)
{ {
token_array * t = split_route_pattern(route, strlen(route)); rnode * n = tree;
return rnode_insert_tokens(tree, t); redge * e = NULL;
char * p = route;
/* length of common prefix */
int dl = 0;
for( int i = 0 ; i < n->edge_len ; i++ ) {
dl = strndiff(route, n->edges[i]->pattern, n->edges[i]->pattern_len);
// printf("dl: %d %s vs %s\n", dl, route, n->edges[i]->pattern );
// no common, consider insert a new edge
if ( dl > 0 ) {
e = n->edges[i];
break;
}
}
if ( dl == 0 ) {
// not found, we should just insert a whole new edge
rnode * child = rnode_create(3);
rnode_add_child(n, strndup(route, route_len) , child);
printf("edge not found, insert one: %s\n", route);
n = child;
return n;
} else if ( dl == e->pattern_len ) { // fully-equal to the pattern of the edge
char * subroute = route + dl;
int subroute_len = route_len - dl;
// there are something more we can insert
if ( subroute_len > 0 ) {
return rnode_insert_routel(e->child, subroute, subroute_len);
} else {
// no more,
e->child->endpoint++; // make it as an endpoint, TODO: put the route value
return e->child;
}
} else if ( dl < e->pattern_len ) {
printf("branch the edge dl: %d\n", dl);
/* it's partically matched with the pattern,
* we should split the end point and make a branch here...
*/
rnode *c1, *c2; // child 1, child 2
redge *e1, *e2; // edge 1, edge 2
char * s1 = e->pattern + dl;
char * s2 = route + dl;
int s1_len = 0, s2_len = 0;
redge **tmp_edges = e->child->edges;
int **tmp_edge_len = e->child->edge_len;
// the suffix edge of the leaf
c1 = rnode_create(3);
s1_len = e->pattern_len - dl;
e1 = redge_create(strndup(s1, s1_len), s1_len, c1);
// printf("edge left: %s\n", e1->pattern);
// Migrate the child edges to the new edge we just created.
for ( int i = 0 ; i < tmp_edge_len ; i++ ) {
rnode_append_edge(c1, tmp_edges[i]);
e->child->edges[i] = NULL;
}
e->child->edge_len = 0;
rnode_append_edge(e->child, e1);
// here is the new edge from.
c2 = rnode_create(3);
s2_len = route_len - dl;
e2 = redge_create(strndup(s2, s2_len), s2_len, c2);
// printf("edge right: %s\n", e2->pattern);
rnode_append_edge(e->child, e2);
// truncate the original edge pattern
free(e->pattern);
e->pattern = strndup(e->pattern, dl);
e->pattern_len = dl;
// Move the child edges to the new suffix edge child
/*
e->child->edge_len = 0;
*/
// move n->edges to c1
c1->endpoint++;
c2->endpoint++;
return c2;
} else if ( dl > 0 ) {
} else {
printf("unexpected condition.");
return NULL;
}
// token_array * t = split_route_pattern(route, strlen(route));
// return rnode_insert_tokens(tree, t);
// n->endpoint++;
return n;
} }
void rnode_dump(rnode * n, int level) { void rnode_dump(rnode * n, int level) {
if ( n->children_len ) { if ( n->edge_len ) {
print_indent(level); print_indent(level);
printf("*\n"); printf("*\n");
for ( int i = 0 ; i < n->children_len ; i++ ) { for ( int i = 0 ; i < n->edge_len ; i++ ) {
redge * e = n->children[i]; redge * e = n->edges[i];
print_indent(level + 1); print_indent(level + 1);
printf("+ \"%s\"\n", e->pattern); printf("+ \"%s\"\n", e->pattern);
rnode_dump( e->child, level + 1); rnode_dump( e->child, level + 1);
@ -214,7 +320,9 @@ redge * redge_create(char * pattern, int pattern_len, rnode * child) {
} }
void redge_free(redge * e) { void redge_free(redge * e) {
free(e->pattern); if (e->pattern) {
free(e->pattern);
}
if ( e->child ) { if ( e->child ) {
rnode_free(e->child); rnode_free(e->child);
} }

View file

@ -11,6 +11,27 @@
#include "str.h" #include "str.h"
#include "token.h" #include "token.h"
int strndiff(char * d1, char * d2, unsigned int n) {
char * o = d1;
while ( *d1 == *d2 && n-- > 0 ) {
d1++;
d2++;
}
return d1 - o;
}
int strdiff(char * d1, char * d2) {
char * o = d1;
while( *d1 == *d2 ) {
d1++;
d2++;
}
return d1 - o;
}
/** /**
* @param char * sep separator * @param char * sep separator
*/ */

View file

@ -17,8 +17,8 @@ START_TEST (test_rnode_construct_uniq)
rnode * child = rnode_create(3); rnode * child = rnode_create(3);
fail_if( rnode_add_child(n, strdup("/add") , child) == FALSE ); // fail_if( rnode_add_child(n, strdup("/add") , child) != NULL );
fail_if( rnode_add_child(n, strdup("/add") , child) != FALSE ); // fail_if( rnode_add_child(n, strdup("/add") , child) != NULL );
rnode_free(n); rnode_free(n);
} }
@ -90,12 +90,23 @@ START_TEST (test_slug_to_pcre)
END_TEST END_TEST
START_TEST (test_rnode_insert_tokens) START_TEST (test_rnode_insert_routel)
{ {
token_array *t;
rnode * n = rnode_create(10); rnode * n = rnode_create(10);
printf("Inserting /foo/bar\n");
rnode_insert_routel(n, "/foo/bar", strlen("/foo/bar") );
rnode_dump(n, 0);
printf("Inserting /foo/zoo\n");
rnode_insert_routel(n, "/foo/zoo", strlen("/foo/zoo") );
rnode_dump(n, 0);
printf("Inserting /f/id\n");
rnode_insert_routel(n, "/f/id", strlen("/f/id") );
rnode_dump(n, 0);
/*
fail_if(n == NULL, "rnode tree"); fail_if(n == NULL, "rnode tree");
t = split_route_pattern("/foo/bar", strlen("/foo/bar") ); t = split_route_pattern("/foo/bar", strlen("/foo/bar") );
@ -112,15 +123,15 @@ START_TEST (test_rnode_insert_tokens)
t = split_route_pattern("/a/jj/kk", strlen("/a/jj/kk") ); t = split_route_pattern("/a/jj/kk", strlen("/a/jj/kk") );
fail_if( rnode_insert_tokens(n , t) == NULL ); fail_if( rnode_insert_tokens(n , t) == NULL );
*/
rnode_dump(n, 0);
/*
fail_if( rnode_lookup(n , "/a/jj/kk" , strlen("/a/jj/kk") ) == NULL ); fail_if( rnode_lookup(n , "/a/jj/kk" , strlen("/a/jj/kk") ) == NULL );
fail_if( rnode_lookup(n , "/a/jj" , strlen("/a/jj") ) != NULL ); fail_if( rnode_lookup(n , "/a/jj" , strlen("/a/jj") ) != NULL );
fail_if( rnode_lookup(n , "/a/jj/kk/ll" , strlen("/a/jj/kk/ll") ) != NULL ); fail_if( rnode_lookup(n , "/a/jj/kk/ll" , strlen("/a/jj/kk/ll") ) != NULL );
fail_if( rnode_lookup(n, "/xxxx", strlen("xxxx") ) != NULL ); fail_if( rnode_lookup(n, "/xxxx", strlen("xxxx") ) != NULL );
*/
// fail_if( rnode_find_edge(n, "/add") == NULL ); // fail_if( rnode_find_edge(n, "/add") == NULL );
@ -201,7 +212,7 @@ Suite* r3_suite (void) {
tcase_add_test(tcase, test_ltrim_slash); tcase_add_test(tcase, test_ltrim_slash);
tcase_add_test(tcase, test_rnode_construct_uniq); tcase_add_test(tcase, test_rnode_construct_uniq);
tcase_add_test(tcase, test_rnode_find_edge); tcase_add_test(tcase, test_rnode_find_edge);
tcase_add_test(tcase, test_rnode_insert_tokens); tcase_add_test(tcase, test_rnode_insert_routel);
tcase_add_test(tcase, test_slug_to_pcre); tcase_add_test(tcase, test_slug_to_pcre);
tcase_add_test(tcase, test_combine_patterns); tcase_add_test(tcase, test_combine_patterns);