END_TEST START_TEST (test_node_construct_and_free) { node * n = r3_tree_create(10); node * another_tree = r3_tree_create(3); r3_tree_free(n); r3_tree_free(another_tree); }
END_TEST START_TEST (test_r3_node_construct_uniq) { node * n = r3_tree_create(10); node * child = r3_tree_create(3); // fail_if( r3_node_add_child(n, strdup("/add") , child) != NULL ); // fail_if( r3_node_add_child(n, strdup("/add") , child) != NULL ); r3_tree_free(n); }
END_TEST START_TEST (test_r3_node_find_edge) { node * n = r3_tree_create(10); node * child = r3_tree_create(3); fail_if( r3_node_add_child(n, strdup("/add") , child) == FALSE ); fail_if( r3_node_find_edge(n, "/add") == NULL ); fail_if( r3_node_find_edge(n, "/bar") != NULL ); r3_tree_free(n); }
END_TEST START_TEST (test_r3_tree_insert_pathl) { node * n = r3_tree_create(10); r3_tree_insert_path(n, "/foo/bar", NULL); // r3_tree_dump(n, 0); r3_tree_insert_path(n, "/foo/zoo", NULL); // r3_tree_dump(n, 0); r3_tree_insert_path(n, "/f/id" , NULL); // r3_tree_dump(n, 0); r3_tree_insert_path(n, "/post/{id}", NULL); // r3_tree_dump(n, 0); r3_tree_insert_path(n, "/post/{handle}", NULL); r3_tree_insert_path(n, "/post/{handle}-{id}", NULL); r3_tree_compile(n); #ifdef DEBUG r3_tree_dump(n, 0); #endif r3_tree_free(n); }
END_TEST START_TEST (test_pcre_patterns_insert) { node * n = r3_tree_create(10); // r3_tree_insert_path(n, "/foo-{user}-{id}", NULL, NULL); // r3_tree_dump(n, 0); r3_tree_insert_path(n, "/post/{handle:\\d+}-{id:\\d+}", NULL); r3_tree_compile(n); r3_tree_dump(n, 0); node *matched; matched = r3_tree_matchl(n, "/post/111-222", strlen("/post/111-222"), NULL); ck_assert(matched); ck_assert_int_gt(matched->endpoint, 0); // incomplete string shouldn't match matched = r3_tree_matchl(n, "/post/111-", strlen("/post/111-"), NULL); ck_assert(matched); ck_assert_int_eq(matched->endpoint, 0); r3_tree_free(n); }
END_TEST START_TEST(test_insert_route) { int var1 = 22; int var2 = 33; route *r1 = r3_route_create("/blog/post"); route *r2 = r3_route_create("/blog/post"); r1->request_method = METHOD_GET; r2->request_method = METHOD_POST; match_entry * entry = match_entry_create("/blog/post"); entry->request_method = METHOD_GET; node * n = r3_tree_create(2); r3_tree_insert_route(n, r1, &var1); r3_tree_insert_route(n, r2, &var2); route *c = r3_tree_match_route(n, entry); fail_if(c == NULL); match_entry_free(entry); r3_route_free(r1); r3_route_free(r2); }
END_TEST START_TEST (test_compile_fail) { node * n = r3_tree_create(10); node * ret; ret = r3_tree_insert_path(n, "/foo/{idx}/{idy:)}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{idx}/{idh:(}", NULL); ck_assert(ret); char * errstr = NULL; r3_tree_compile(n, &errstr); ck_assert(errstr); fprintf(stderr, "Compile failed: %s\n", errstr); free(errstr); r3_tree_dump(n, 0); r3_tree_free(n); }
/** * branch the edge pattern at "dl" offset, * insert a dummy child between the edges. * * * A -> [prefix..suffix] -> B * A -> [prefix] -> B -> [suffix] -> New Child (Copy Data, Edges from B) * */ node * r3_edge_branch(edge *e, int dl) { node *new_child; edge *e1; char * s1 = e->pattern + dl; int s1_len = 0; edge **tmp_edges = e->child->edges; int tmp_edge_len = e->child->edge_len; // the suffix edge of the leaf new_child = r3_tree_create(3); s1_len = e->pattern_len - dl; e1 = r3_edge_create(zstrndup(s1, s1_len), s1_len, new_child); // Migrate the child edges to the new edge we just created. for ( int i = 0 ; i < tmp_edge_len ; i++ ) { r3_node_append_edge(new_child, tmp_edges[i]); e->child->edges[i] = NULL; } e->child->edge_len = 0; new_child->endpoint = e->child->endpoint; e->child->endpoint = 0; // reset endpoint r3_node_append_edge(e->child, e1); new_child->data = e->child->data; // copy data pointer e->child->data = NULL; // truncate the original edge pattern char *op = e->pattern; e->pattern = zstrndup(e->pattern, dl); e->pattern_len = dl; return new_child; }
END_TEST START_TEST (test_find_common_prefix_middle) { node * n = r3_tree_create(10); edge * e = r3_edge_createl(zstrdup("/foo/{slug}/hate"), sizeof("/foo/{slug}/hate")-1, NULL); r3_node_append_edge(n,e); int prefix_len; edge *ret_edge = NULL; char *errstr = NULL; errstr = NULL; ret_edge = r3_node_find_common_prefix(n, "/foo/{slug}/bar", sizeof("/foo/{slug}/bar")-1, &prefix_len, &errstr); ck_assert(ret_edge); ck_assert_int_eq(prefix_len, 12); SAFE_FREE(errstr); errstr = NULL; ret_edge = r3_node_find_common_prefix(n, "/fo/{slug}/bar", sizeof("/fo/{slug}/bar")-1, &prefix_len, &errstr); ck_assert(ret_edge); ck_assert_int_eq(prefix_len, 3); SAFE_FREE(errstr); r3_tree_free(n); }
END_TEST static node * create_simple_str_tree() { node * n; n = r3_tree_create(10); r3_tree_insert_path(n, "/zoo", NULL); r3_tree_insert_path(n, "/foo", NULL); r3_tree_insert_path(n, "/bar", NULL); r3_tree_compile(n, NULL); return n; }
static PyObject* Trie_new(PyObject* type, PyObject* args, PyObject* kwds) { Trie *self; self = (Trie *)type->tp_alloc(type, 0); if (self != NULL) { // TODO: cap self->node = r3_tree_create(100); } return (PyObject *)self; }
END_TEST START_TEST(test_pcre_pattern_more) { match_entry * entry; entry = match_entry_createl( "/user/123" , strlen("/user/123") ); node * n = r3_tree_create(10); int var0 = 5; int var1 = 100; int var2 = 200; int var3 = 300; info("var0: %p\n", &var0); info("var1: %p\n", &var1); info("var2: %p\n", &var2); info("var3: %p\n", &var3); r3_tree_insert_path(n, "/user/{id:\\d+}", &var1); r3_tree_insert_path(n, "/user2/{id:\\d+}", &var2); r3_tree_insert_path(n, "/user3/{id:\\d{3}}", &var3); r3_tree_insert_path(n, "/user", &var0); r3_tree_compile(n, NULL); r3_tree_dump(n, 0); node *matched; matched = r3_tree_matchl(n, "/user/123", strlen("/user/123"), entry); ck_assert(matched); ck_assert(entry->vars->len > 0); ck_assert_str_eq(entry->vars->tokens[0],"123"); info("matched %p\n", matched->data); info("matched %p\n", matched->data); ck_assert_int_eq( *((int*) matched->data), var1); matched = r3_tree_matchl(n, "/user2/123", strlen("/user2/123"), entry); ck_assert(matched); ck_assert(entry->vars->len > 0); ck_assert_str_eq(entry->vars->tokens[0],"123"); ck_assert_int_eq( *((int*)matched->data), var2); matched = r3_tree_matchl(n, "/user3/123", strlen("/user3/123"), entry); ck_assert(matched); ck_assert(entry->vars->len > 0); ck_assert_str_eq(entry->vars->tokens[0],"123"); ck_assert_int_eq( *((int*)matched->data), var3); r3_tree_free(n); }
END_TEST START_TEST (test_pcre_patterns_insert) { node * n = r3_tree_create(10); // r3_tree_insert_path(n, "/foo-{user}-{id}", NULL, NULL); r3_tree_insert_path(n, "/post/{handle:\\d+}-{id:\\d+}", NULL); r3_tree_insert_path(n, "/post/foo", NULL); r3_tree_insert_path(n, "/post/bar", NULL); char *errstr = NULL; int errcode; errcode = r3_tree_compile(n, &errstr); ck_assert(errcode == 0); // no error // r3_tree_dump(n, 0); node *matched; matched = r3_tree_match(n, "/post/foo", NULL); ck_assert(matched); ck_assert(matched->endpoint > 0); matched = r3_tree_match(n, "/post/bar", NULL); ck_assert(matched); ck_assert(matched->endpoint > 0); matched = r3_tree_match(n, "/post/kkkfoo", NULL); ck_assert(!matched); matched = r3_tree_match(n, "/post/kkkbar", NULL); ck_assert(!matched); matched = r3_tree_matchl(n, "/post/111-222", strlen("/post/111-222"), NULL); ck_assert(matched); ck_assert(matched->endpoint > 0); // incomplete string shouldn't match matched = r3_tree_matchl(n, "/post/111-", strlen("/post/111-"), NULL); ck_assert(! matched); r3_tree_free(n); }
END_TEST START_TEST (test_insert_pathl) { node * n = r3_tree_create(10); node * ret; ret = r3_tree_insert_path(n, "/foo/bar", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/zoo", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{id}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{number:\\d+}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{name:\\w+}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{name:\\d+}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{name:\\d{5}}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{idx}/{idy}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/foo/{idx}/{idh}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/f/id" , NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/post/{id}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/post/{handle}", NULL); ck_assert(ret); ret = r3_tree_insert_path(n, "/post/{handle}-{id}", NULL); ck_assert(ret); char * errstr = NULL; r3_tree_compile(n, &errstr); ck_assert(errstr == NULL); r3_tree_dump(n, 0); r3_tree_free(n); }
END_TEST START_TEST (test_incomplete_slug_path) { node * n = r3_tree_create(10); node * ret_node; // r3_tree_insert_path(n, "/foo-{user}-{id}", NULL, NULL); ret_node = r3_tree_insert_path(n, "/post/{handle", NULL); assert(!ret_node); ret_node = r3_tree_insert_path(n, "/post/{handle:\\", NULL); assert(!ret_node); ret_node = r3_tree_insert_path(n, "/post/{handle:\\d", NULL); assert(!ret_node); ret_node = r3_tree_insert_path(n, "/post/{handle:\\d{", NULL); assert(!ret_node); ret_node = r3_tree_insert_path(n, "/post/{handle:\\d{3", NULL); assert(!ret_node); r3_tree_insert_path(n, "/post/{handle:\\d{3}", NULL); r3_tree_insert_path(n, "/post/{handle:\\d{3}}/{", NULL); r3_tree_insert_path(n, "/post/{handle:\\d{3}}/{a", NULL); r3_tree_insert_path(n, "/post/{handle:\\d{3}}/{a}", NULL); ret_node = r3_tree_insert_path(n, "/users/{idx:\\d{3}}/{idy}", NULL); ck_assert(ret_node); // OK to insert, but should return error when compiling patterns node * ret_node2 = r3_tree_insert_path(n, "/users/{idx:\\d{3}}/{idy:aaa}", NULL); ck_assert(ret_node2); ck_assert(ret_node2 != ret_node); // make sure it's another node char *errstr = NULL; r3_tree_compile(n, &errstr); ck_assert(errstr == NULL); // no error r3_tree_dump(n, NULL); r3_tree_free(n); }
END_TEST START_TEST (test_find_common_prefix_same_pattern2) { node * n = r3_tree_create(10); edge * e = r3_edge_createl(zstrdup("{slug:xxx}/hate"), sizeof("{slug:xxx}/hate")-1, NULL); r3_node_append_edge(n,e); int prefix_len; edge *ret_edge = NULL; prefix_len = 0; ret_edge = r3_node_find_common_prefix(n, "{slug:yyy}/hate", sizeof("{slug:yyy}/hate")-1, &prefix_len, NULL); ck_assert(ret_edge); ck_assert_int_eq(prefix_len, 0); r3_tree_free(n); }
END_TEST START_TEST(test_pcre_pattern_simple) { match_entry * entry; entry = match_entry_createl( "/user/123" , strlen("/user/123") ); node * n = r3_tree_create(10); r3_tree_insert_path(n, "/user/{id:\\d+}", NULL); r3_tree_insert_path(n, "/user", NULL); r3_tree_compile(n); // r3_tree_dump(n, 0); node *matched; matched = r3_tree_matchl(n, "/user/123", strlen("/user/123"), entry); fail_if(matched == NULL); ck_assert_int_gt(entry->vars->len, 0); ck_assert_str_eq(entry->vars->tokens[0],"123"); }
/** * r3_edge_branch splits the edge and append the rest part as the child of the * first level child * * branch the edge pattern at "dl" offset, * and insert a dummy child between the edges. * * A -> [EDGE: abcdefg] -> B -> [EDGE:branch1], [EDGE:branch2] * A -> [EDGE: abcd] -> B1 -> [efg] -> B2 (new child with copied data from B) * */ R3Node * r3_edge_branch(R3Edge *e, int dl) { R3Node * new_child; R3Edge * new_edge; // the rest string char * s1 = e->pattern.base + dl; int s1_len = e->pattern.len - dl; // the suffix edge of the leaf new_child = r3_tree_create(3); new_edge = r3_node_append_edge(new_child); r3_edge_initl(new_edge, s1, s1_len, e->child); e->child = new_child; // truncate the original edge pattern e->pattern.len = dl; return new_child; }
END_TEST START_TEST (test_gvc_render_file) { node * n = r3_tree_create(1); r3_tree_insert_path(n, "/foo/bar/baz", NULL); r3_tree_insert_path(n, "/foo/bar/qux", NULL); r3_tree_insert_path(n, "/foo/bar/quux", NULL); r3_tree_insert_path(n, "/foo/bar/corge", NULL); r3_tree_insert_path(n, "/foo/bar/grault", NULL); r3_tree_insert_path(n, "/garply/grault/foo", NULL); r3_tree_insert_path(n, "/garply/grault/bar", NULL); r3_tree_insert_path(n, "/user/{id}", NULL); r3_tree_insert_path(n, "/post/{title:\\w+}", NULL); r3_tree_compile(n); r3_tree_render_file(n, "png", "check_gvc.png"); r3_tree_free(n); }
END_TEST /** * Test for (\d{2})/([^/]+) */ START_TEST (test_pcre_patterns_insert_3) { node * n = r3_tree_create(10); printf("Inserting /post/{idx:\\d{2}}/{idy}\n"); r3_tree_insert_path(n, "/post/{idx:\\d{2}}/{idy}", NULL); r3_tree_dump(n, 0); printf("Inserting /zoo\n"); r3_tree_insert_path(n, "/zoo", NULL); r3_tree_dump(n, 0); r3_tree_insert_path(n, "/foo", NULL); r3_tree_insert_path(n, "/bar", NULL); char *errstr = NULL; r3_tree_compile(n, &errstr); r3_tree_dump(n, 0); node *matched; matched = r3_tree_match(n, "/post/11/22", NULL); ck_assert((int)matched); matched = r3_tree_match(n, "/post/11", NULL); ck_assert(!matched); matched = r3_tree_match(n, "/post/11/", NULL); ck_assert(!matched); /* matched = r3_tree_match(n, "/post/113", NULL); ck_assert(!matched); */ }
END_TEST START_TEST (test_insert_pathl_fail) { node * n = r3_tree_create(10); node * ret; char *errstr = NULL; ret = r3_tree_insert_pathl_ex(n, "/foo/{name:\\d{5}", strlen("/foo/{name:\\d{5}"), NULL, NULL, &errstr); ck_assert(ret == NULL); ck_assert(errstr != NULL); printf("%s\n", errstr); // Returns Incomplete slug pattern. PATTERN (16): '/foo/{name:\d{5}', OFFSET: 16, STATE: 1 SAFE_FREE(errstr); errstr = NULL; r3_tree_compile(n, &errstr); ck_assert(errstr == NULL); r3_tree_free(n); }
END_TEST /** * Test for \d{2}/\d{2} */ START_TEST (test_pcre_patterns_insert_2) { node * n = r3_tree_create(10); r3_tree_insert_path(n, "/post/{idx:\\d{2}}/{idy:\\d{2}}", NULL); r3_tree_insert_path(n, "/zoo", NULL); r3_tree_insert_path(n, "/foo", NULL); r3_tree_insert_path(n, "/bar", NULL); char *errstr = NULL; r3_tree_compile(n, &errstr); r3_tree_dump(n, 0); node *matched; matched = r3_tree_match(n, "/post/11/22", NULL); ck_assert((int)matched); ck_assert(matched->endpoint > 0); }
/** * Return the last inserted node. */ node * _r3_tree_insert_pathl(node *tree, char *path, int path_len, route * route, void * data) { node * n = tree; edge * e = NULL; /* length of common prefix */ int prefix_len = 0; for( int i = 0 ; i < n->edge_len ; i++ ) { prefix_len = strndiff(path, n->edges[i]->pattern, n->edges[i]->pattern_len); // printf("prefix_len: %d %s vs %s\n", prefix_len, path, n->edges[i]->pattern ); // no common, consider insert a new edge if ( prefix_len > 0 ) { e = n->edges[i]; break; } } // branch the edge at correct position (avoid broken slugs) char *slug_s; if ( (slug_s = inside_slug(path, path_len, path + prefix_len)) != NULL ) { prefix_len = slug_s - path; } // common prefix not found, insert a new edge for this pattern if ( prefix_len == 0 ) { // there are two more slugs, we should break them into several parts if ( count_slug(path, path_len) > 1 ) { char *p = find_slug_placeholder(path, NULL); #ifdef DEBUG assert(p); #endif // find the next one p = find_slug_placeholder(p + 1, NULL); #ifdef DEBUG assert(p); #endif // insert the first one edge, and break at "p" node * child = r3_tree_create(3); r3_node_add_child(n, strndup(path, (int)(p - path)), child); // and insert the rest part to the child return _r3_tree_insert_pathl(child, p, path_len - (int)(p - path), route, data); } else { node * child = r3_tree_create(3); r3_node_add_child(n, strndup(path, path_len) , child); // info("edge not found, insert one: %s\n", path); child->data = data; child->endpoint++; if (route) { route->data = data; r3_node_append_route(child, route); } return child; } } else if ( prefix_len == e->pattern_len ) { // fully-equal to the pattern of the edge char * subpath = path + prefix_len; int subpath_len = path_len - prefix_len; // there are something more we can insert if ( subpath_len > 0 ) { return _r3_tree_insert_pathl(e->child, subpath, subpath_len, route, data); } else { // there are no more path to insert // see if there is an endpoint already if (e->child->endpoint) { // XXX: return an error code instead of NULL return NULL; } e->child->endpoint++; // make it as an endpoint e->child->data = data; if (route) { route->data = data; r3_node_append_route(e->child, route); } return e->child; } } else if ( prefix_len < e->pattern_len ) { // printf("branch the edge prefix_len: %d\n", prefix_len); /* it's partially matched with the pattern, * we should split the end point and make a branch here... */ char * s2 = path + prefix_len; int s2_len = path_len - prefix_len; r3_edge_branch(e, prefix_len); return _r3_tree_insert_pathl(e->child, s2 , s2_len, route , data); } else { printf("unexpected route."); return NULL; } return n; }
END_TEST START_TEST(benchmark_str) { match_entry * entry = match_entry_createl("/blog/post", strlen("/blog/post") ); node * n = r3_tree_create(1); int route_data = 999; r3_tree_insert_path(n, "/foo/bar/baz", NULL); r3_tree_insert_path(n, "/foo/bar/qux", NULL); r3_tree_insert_path(n, "/foo/bar/quux", NULL); r3_tree_insert_path(n, "/foo/bar/corge", NULL); r3_tree_insert_path(n, "/foo/bar/grault", NULL); r3_tree_insert_path(n, "/foo/bar/garply", NULL); r3_tree_insert_path(n, "/foo/baz/bar", NULL); r3_tree_insert_path(n, "/foo/baz/qux", NULL); r3_tree_insert_path(n, "/foo/baz/quux", NULL); r3_tree_insert_path(n, "/foo/baz/corge", NULL); r3_tree_insert_path(n, "/foo/baz/grault", NULL); r3_tree_insert_path(n, "/foo/baz/garply", NULL); r3_tree_insert_path(n, "/foo/qux/bar", NULL); r3_tree_insert_path(n, "/foo/qux/baz", NULL); r3_tree_insert_path(n, "/foo/qux/quux", NULL); r3_tree_insert_path(n, "/foo/qux/corge", NULL); r3_tree_insert_path(n, "/foo/qux/grault", NULL); r3_tree_insert_path(n, "/foo/qux/garply", NULL); r3_tree_insert_path(n, "/foo/quux/bar", NULL); r3_tree_insert_path(n, "/foo/quux/baz", NULL); r3_tree_insert_path(n, "/foo/quux/qux", NULL); r3_tree_insert_path(n, "/foo/quux/corge", NULL); r3_tree_insert_path(n, "/foo/quux/grault", NULL); r3_tree_insert_path(n, "/foo/quux/garply", NULL); r3_tree_insert_path(n, "/foo/corge/bar", NULL); r3_tree_insert_path(n, "/foo/corge/baz", NULL); r3_tree_insert_path(n, "/foo/corge/qux", NULL); r3_tree_insert_path(n, "/foo/corge/quux", NULL); r3_tree_insert_path(n, "/foo/corge/grault", NULL); r3_tree_insert_path(n, "/foo/corge/garply", NULL); r3_tree_insert_path(n, "/foo/grault/bar", NULL); r3_tree_insert_path(n, "/foo/grault/baz", NULL); r3_tree_insert_path(n, "/foo/grault/qux", NULL); r3_tree_insert_path(n, "/foo/grault/quux", NULL); r3_tree_insert_path(n, "/foo/grault/corge", NULL); r3_tree_insert_path(n, "/foo/grault/garply", NULL); r3_tree_insert_path(n, "/foo/garply/bar", NULL); r3_tree_insert_path(n, "/foo/garply/baz", NULL); r3_tree_insert_path(n, "/foo/garply/qux", NULL); r3_tree_insert_path(n, "/foo/garply/quux", NULL); r3_tree_insert_path(n, "/foo/garply/corge", NULL); r3_tree_insert_path(n, "/foo/garply/grault", NULL); r3_tree_insert_path(n, "/bar/foo/baz", NULL); r3_tree_insert_path(n, "/bar/foo/qux", NULL); r3_tree_insert_path(n, "/bar/foo/quux", NULL); r3_tree_insert_path(n, "/bar/foo/corge", NULL); r3_tree_insert_path(n, "/bar/foo/grault", NULL); r3_tree_insert_path(n, "/bar/foo/garply", NULL); r3_tree_insert_path(n, "/bar/baz/foo", NULL); r3_tree_insert_path(n, "/bar/baz/qux", NULL); r3_tree_insert_path(n, "/bar/baz/quux", NULL); r3_tree_insert_path(n, "/bar/baz/corge", NULL); r3_tree_insert_path(n, "/bar/baz/grault", NULL); r3_tree_insert_path(n, "/bar/baz/garply", NULL); r3_tree_insert_path(n, "/bar/qux/foo", NULL); r3_tree_insert_path(n, "/bar/qux/baz", NULL); r3_tree_insert_path(n, "/bar/qux/quux", NULL); r3_tree_insert_path(n, "/bar/qux/corge", NULL); r3_tree_insert_path(n, "/bar/qux/grault", NULL); r3_tree_insert_path(n, "/bar/qux/garply", NULL); r3_tree_insert_path(n, "/bar/quux/foo", NULL); r3_tree_insert_path(n, "/bar/quux/baz", NULL); r3_tree_insert_path(n, "/bar/quux/qux", NULL); r3_tree_insert_path(n, "/bar/quux/corge", NULL); r3_tree_insert_path(n, "/bar/quux/grault", NULL); r3_tree_insert_path(n, "/bar/quux/garply", NULL); r3_tree_insert_path(n, "/bar/corge/foo", NULL); r3_tree_insert_path(n, "/bar/corge/baz", NULL); r3_tree_insert_path(n, "/bar/corge/qux", NULL); r3_tree_insert_path(n, "/bar/corge/quux", NULL); r3_tree_insert_path(n, "/bar/corge/grault", NULL); r3_tree_insert_path(n, "/bar/corge/garply", NULL); r3_tree_insert_path(n, "/bar/grault/foo", NULL); r3_tree_insert_path(n, "/bar/grault/baz", NULL); r3_tree_insert_path(n, "/bar/grault/qux", NULL); r3_tree_insert_path(n, "/bar/grault/quux", NULL); r3_tree_insert_path(n, "/bar/grault/corge", NULL); r3_tree_insert_path(n, "/bar/grault/garply", NULL); r3_tree_insert_path(n, "/bar/garply/foo", NULL); r3_tree_insert_path(n, "/bar/garply/baz", NULL); r3_tree_insert_path(n, "/bar/garply/qux", NULL); r3_tree_insert_path(n, "/bar/garply/quux", NULL); r3_tree_insert_path(n, "/bar/garply/corge", NULL); r3_tree_insert_path(n, "/bar/garply/grault", NULL); r3_tree_insert_path(n, "/baz/foo/bar", NULL); r3_tree_insert_path(n, "/baz/foo/qux", NULL); r3_tree_insert_path(n, "/baz/foo/quux", NULL); r3_tree_insert_path(n, "/baz/foo/corge", NULL); r3_tree_insert_path(n, "/baz/foo/grault", NULL); r3_tree_insert_path(n, "/baz/foo/garply", NULL); r3_tree_insert_path(n, "/baz/bar/foo", NULL); r3_tree_insert_path(n, "/baz/bar/qux", NULL); r3_tree_insert_path(n, "/baz/bar/quux", NULL); r3_tree_insert_path(n, "/baz/bar/corge", NULL); r3_tree_insert_path(n, "/baz/bar/grault", NULL); r3_tree_insert_path(n, "/baz/bar/garply", NULL); r3_tree_insert_path(n, "/baz/qux/foo", NULL); r3_tree_insert_path(n, "/baz/qux/bar", NULL); r3_tree_insert_path(n, "/baz/qux/quux", NULL); r3_tree_insert_path(n, "/baz/qux/corge", NULL); r3_tree_insert_path(n, "/baz/qux/grault", NULL); r3_tree_insert_path(n, "/baz/qux/garply", NULL); r3_tree_insert_path(n, "/baz/quux/foo", NULL); r3_tree_insert_path(n, "/baz/quux/bar", NULL); r3_tree_insert_path(n, "/baz/quux/qux", NULL); r3_tree_insert_path(n, "/baz/quux/corge", NULL); r3_tree_insert_path(n, "/baz/quux/grault", NULL); r3_tree_insert_path(n, "/baz/quux/garply", NULL); r3_tree_insert_path(n, "/baz/corge/foo", NULL); r3_tree_insert_path(n, "/baz/corge/bar", NULL); r3_tree_insert_path(n, "/baz/corge/qux", NULL); r3_tree_insert_path(n, "/baz/corge/quux", NULL); r3_tree_insert_path(n, "/baz/corge/grault", NULL); r3_tree_insert_path(n, "/baz/corge/garply", NULL); r3_tree_insert_path(n, "/baz/grault/foo", NULL); r3_tree_insert_path(n, "/baz/grault/bar", NULL); r3_tree_insert_path(n, "/baz/grault/qux", NULL); r3_tree_insert_path(n, "/baz/grault/quux", NULL); r3_tree_insert_path(n, "/baz/grault/corge", NULL); r3_tree_insert_path(n, "/baz/grault/garply", NULL); r3_tree_insert_path(n, "/baz/garply/foo", NULL); r3_tree_insert_path(n, "/baz/garply/bar", NULL); r3_tree_insert_path(n, "/baz/garply/qux", NULL); r3_tree_insert_path(n, "/baz/garply/quux", NULL); r3_tree_insert_path(n, "/baz/garply/corge", NULL); r3_tree_insert_path(n, "/baz/garply/grault", NULL); r3_tree_insert_path(n, "/qux/foo/bar", NULL); r3_tree_insert_path(n, "/qux/foo/baz", NULL); r3_tree_insert_path(n, "/qux/foo/quux", NULL); r3_tree_insert_path(n, "/qux/foo/corge", NULL); r3_tree_insert_path(n, "/qux/foo/grault", NULL); r3_tree_insert_path(n, "/qux/foo/garply", NULL); r3_tree_insert_path(n, "/qux/bar/foo", NULL); r3_tree_insert_path(n, "/qux/bar/baz", NULL); r3_tree_insert_path(n, "/qux/bar/quux", NULL); r3_tree_insert_path(n, "/qux/bar/corge", &route_data); r3_tree_insert_path(n, "/qux/bar/grault", NULL); r3_tree_insert_path(n, "/qux/bar/garply", NULL); r3_tree_insert_path(n, "/qux/baz/foo", NULL); r3_tree_insert_path(n, "/qux/baz/bar", NULL); r3_tree_insert_path(n, "/qux/baz/quux", NULL); r3_tree_insert_path(n, "/qux/baz/corge", NULL); r3_tree_insert_path(n, "/qux/baz/grault", NULL); r3_tree_insert_path(n, "/qux/baz/garply", NULL); r3_tree_insert_path(n, "/qux/quux/foo", NULL); r3_tree_insert_path(n, "/qux/quux/bar", NULL); r3_tree_insert_path(n, "/qux/quux/baz", NULL); r3_tree_insert_path(n, "/qux/quux/corge", NULL); r3_tree_insert_path(n, "/qux/quux/grault", NULL); r3_tree_insert_path(n, "/qux/quux/garply", NULL); r3_tree_insert_path(n, "/qux/corge/foo", NULL); r3_tree_insert_path(n, "/qux/corge/bar", NULL); r3_tree_insert_path(n, "/qux/corge/baz", NULL); r3_tree_insert_path(n, "/qux/corge/quux", NULL); r3_tree_insert_path(n, "/qux/corge/grault", NULL); r3_tree_insert_path(n, "/qux/corge/garply", NULL); r3_tree_insert_path(n, "/qux/grault/foo", NULL); r3_tree_insert_path(n, "/qux/grault/bar", NULL); r3_tree_insert_path(n, "/qux/grault/baz", NULL); r3_tree_insert_path(n, "/qux/grault/quux", NULL); r3_tree_insert_path(n, "/qux/grault/corge", NULL); r3_tree_insert_path(n, "/qux/grault/garply", NULL); r3_tree_insert_path(n, "/qux/garply/foo", NULL); r3_tree_insert_path(n, "/qux/garply/bar", NULL); r3_tree_insert_path(n, "/qux/garply/baz", NULL); r3_tree_insert_path(n, "/qux/garply/quux", NULL); r3_tree_insert_path(n, "/qux/garply/corge", NULL); r3_tree_insert_path(n, "/qux/garply/grault", NULL); r3_tree_insert_path(n, "/quux/foo/bar", NULL); r3_tree_insert_path(n, "/quux/foo/baz", NULL); r3_tree_insert_path(n, "/quux/foo/qux", NULL); r3_tree_insert_path(n, "/quux/foo/corge", NULL); r3_tree_insert_path(n, "/quux/foo/grault", NULL); r3_tree_insert_path(n, "/quux/foo/garply", NULL); r3_tree_insert_path(n, "/quux/bar/foo", NULL); r3_tree_insert_path(n, "/quux/bar/baz", NULL); r3_tree_insert_path(n, "/quux/bar/qux", NULL); r3_tree_insert_path(n, "/quux/bar/corge", NULL); r3_tree_insert_path(n, "/quux/bar/grault", NULL); r3_tree_insert_path(n, "/quux/bar/garply", NULL); r3_tree_insert_path(n, "/quux/baz/foo", NULL); r3_tree_insert_path(n, "/quux/baz/bar", NULL); r3_tree_insert_path(n, "/quux/baz/qux", NULL); r3_tree_insert_path(n, "/quux/baz/corge", NULL); r3_tree_insert_path(n, "/quux/baz/grault", NULL); r3_tree_insert_path(n, "/quux/baz/garply", NULL); r3_tree_insert_path(n, "/quux/qux/foo", NULL); r3_tree_insert_path(n, "/quux/qux/bar", NULL); r3_tree_insert_path(n, "/quux/qux/baz", NULL); r3_tree_insert_path(n, "/quux/qux/corge", NULL); r3_tree_insert_path(n, "/quux/qux/grault", NULL); r3_tree_insert_path(n, "/quux/qux/garply", NULL); r3_tree_insert_path(n, "/quux/corge/foo", NULL); r3_tree_insert_path(n, "/quux/corge/bar", NULL); r3_tree_insert_path(n, "/quux/corge/baz", NULL); r3_tree_insert_path(n, "/quux/corge/qux", NULL); r3_tree_insert_path(n, "/quux/corge/grault", NULL); r3_tree_insert_path(n, "/quux/corge/garply", NULL); r3_tree_insert_path(n, "/quux/grault/foo", NULL); r3_tree_insert_path(n, "/quux/grault/bar", NULL); r3_tree_insert_path(n, "/quux/grault/baz", NULL); r3_tree_insert_path(n, "/quux/grault/qux", NULL); r3_tree_insert_path(n, "/quux/grault/corge", NULL); r3_tree_insert_path(n, "/quux/grault/garply", NULL); r3_tree_insert_path(n, "/quux/garply/foo", NULL); r3_tree_insert_path(n, "/quux/garply/bar", NULL); r3_tree_insert_path(n, "/quux/garply/baz", NULL); r3_tree_insert_path(n, "/quux/garply/qux", NULL); r3_tree_insert_path(n, "/quux/garply/corge", NULL); r3_tree_insert_path(n, "/quux/garply/grault", NULL); r3_tree_insert_path(n, "/corge/foo/bar", NULL); r3_tree_insert_path(n, "/corge/foo/baz", NULL); r3_tree_insert_path(n, "/corge/foo/qux", NULL); r3_tree_insert_path(n, "/corge/foo/quux", NULL); r3_tree_insert_path(n, "/corge/foo/grault", NULL); r3_tree_insert_path(n, "/corge/foo/garply", NULL); r3_tree_insert_path(n, "/corge/bar/foo", NULL); r3_tree_insert_path(n, "/corge/bar/baz", NULL); r3_tree_insert_path(n, "/corge/bar/qux", NULL); r3_tree_insert_path(n, "/corge/bar/quux", NULL); r3_tree_insert_path(n, "/corge/bar/grault", NULL); r3_tree_insert_path(n, "/corge/bar/garply", NULL); r3_tree_insert_path(n, "/corge/baz/foo", NULL); r3_tree_insert_path(n, "/corge/baz/bar", NULL); r3_tree_insert_path(n, "/corge/baz/qux", NULL); r3_tree_insert_path(n, "/corge/baz/quux", NULL); r3_tree_insert_path(n, "/corge/baz/grault", NULL); r3_tree_insert_path(n, "/corge/baz/garply", NULL); r3_tree_insert_path(n, "/corge/qux/foo", NULL); r3_tree_insert_path(n, "/corge/qux/bar", NULL); r3_tree_insert_path(n, "/corge/qux/baz", NULL); r3_tree_insert_path(n, "/corge/qux/quux", NULL); r3_tree_insert_path(n, "/corge/qux/grault", NULL); r3_tree_insert_path(n, "/corge/qux/garply", NULL); r3_tree_insert_path(n, "/corge/quux/foo", NULL); r3_tree_insert_path(n, "/corge/quux/bar", NULL); r3_tree_insert_path(n, "/corge/quux/baz", NULL); r3_tree_insert_path(n, "/corge/quux/qux", NULL); r3_tree_insert_path(n, "/corge/quux/grault", NULL); r3_tree_insert_path(n, "/corge/quux/garply", NULL); r3_tree_insert_path(n, "/corge/grault/foo", NULL); r3_tree_insert_path(n, "/corge/grault/bar", NULL); r3_tree_insert_path(n, "/corge/grault/baz", NULL); r3_tree_insert_path(n, "/corge/grault/qux", NULL); r3_tree_insert_path(n, "/corge/grault/quux", NULL); r3_tree_insert_path(n, "/corge/grault/garply", NULL); r3_tree_insert_path(n, "/corge/garply/foo", NULL); r3_tree_insert_path(n, "/corge/garply/bar", NULL); r3_tree_insert_path(n, "/corge/garply/baz", NULL); r3_tree_insert_path(n, "/corge/garply/qux", NULL); r3_tree_insert_path(n, "/corge/garply/quux", NULL); r3_tree_insert_path(n, "/corge/garply/grault", NULL); r3_tree_insert_path(n, "/grault/foo/bar", NULL); r3_tree_insert_path(n, "/grault/foo/baz", NULL); r3_tree_insert_path(n, "/grault/foo/qux", NULL); r3_tree_insert_path(n, "/grault/foo/quux", NULL); r3_tree_insert_path(n, "/grault/foo/corge", NULL); r3_tree_insert_path(n, "/grault/foo/garply", NULL); r3_tree_insert_path(n, "/grault/bar/foo", NULL); r3_tree_insert_path(n, "/grault/bar/baz", NULL); r3_tree_insert_path(n, "/grault/bar/qux", NULL); r3_tree_insert_path(n, "/grault/bar/quux", NULL); r3_tree_insert_path(n, "/grault/bar/corge", NULL); r3_tree_insert_path(n, "/grault/bar/garply", NULL); r3_tree_insert_path(n, "/grault/baz/foo", NULL); r3_tree_insert_path(n, "/grault/baz/bar", NULL); r3_tree_insert_path(n, "/grault/baz/qux", NULL); r3_tree_insert_path(n, "/grault/baz/quux", NULL); r3_tree_insert_path(n, "/grault/baz/corge", NULL); r3_tree_insert_path(n, "/grault/baz/garply", NULL); r3_tree_insert_path(n, "/grault/qux/foo", NULL); r3_tree_insert_path(n, "/grault/qux/bar", NULL); r3_tree_insert_path(n, "/grault/qux/baz", NULL); r3_tree_insert_path(n, "/grault/qux/quux", NULL); r3_tree_insert_path(n, "/grault/qux/corge", NULL); r3_tree_insert_path(n, "/grault/qux/garply", NULL); r3_tree_insert_path(n, "/grault/quux/foo", NULL); r3_tree_insert_path(n, "/grault/quux/bar", NULL); r3_tree_insert_path(n, "/grault/quux/baz", NULL); r3_tree_insert_path(n, "/grault/quux/qux", NULL); r3_tree_insert_path(n, "/grault/quux/corge", NULL); r3_tree_insert_path(n, "/grault/quux/garply", NULL); r3_tree_insert_path(n, "/grault/corge/foo", NULL); r3_tree_insert_path(n, "/grault/corge/bar", NULL); r3_tree_insert_path(n, "/grault/corge/baz", NULL); r3_tree_insert_path(n, "/grault/corge/qux", NULL); r3_tree_insert_path(n, "/grault/corge/quux", NULL); r3_tree_insert_path(n, "/grault/corge/garply", NULL); r3_tree_insert_path(n, "/grault/garply/foo", NULL); r3_tree_insert_path(n, "/grault/garply/bar", NULL); r3_tree_insert_path(n, "/grault/garply/baz", NULL); r3_tree_insert_path(n, "/grault/garply/qux", NULL); r3_tree_insert_path(n, "/grault/garply/quux", NULL); r3_tree_insert_path(n, "/grault/garply/corge", NULL); r3_tree_insert_path(n, "/garply/foo/bar", NULL); r3_tree_insert_path(n, "/garply/foo/baz", NULL); r3_tree_insert_path(n, "/garply/foo/qux", NULL); r3_tree_insert_path(n, "/garply/foo/quux", NULL); r3_tree_insert_path(n, "/garply/foo/corge", NULL); r3_tree_insert_path(n, "/garply/foo/grault", NULL); r3_tree_insert_path(n, "/garply/bar/foo", NULL); r3_tree_insert_path(n, "/garply/bar/baz", NULL); r3_tree_insert_path(n, "/garply/bar/qux", NULL); r3_tree_insert_path(n, "/garply/bar/quux", NULL); r3_tree_insert_path(n, "/garply/bar/corge", NULL); r3_tree_insert_path(n, "/garply/bar/grault", NULL); r3_tree_insert_path(n, "/garply/baz/foo", NULL); r3_tree_insert_path(n, "/garply/baz/bar", NULL); r3_tree_insert_path(n, "/garply/baz/qux", NULL); r3_tree_insert_path(n, "/garply/baz/quux", NULL); r3_tree_insert_path(n, "/garply/baz/corge", NULL); r3_tree_insert_path(n, "/garply/baz/grault", NULL); r3_tree_insert_path(n, "/garply/qux/foo", NULL); r3_tree_insert_path(n, "/garply/qux/bar", NULL); r3_tree_insert_path(n, "/garply/qux/baz", NULL); r3_tree_insert_path(n, "/garply/qux/quux", NULL); r3_tree_insert_path(n, "/garply/qux/corge", NULL); r3_tree_insert_path(n, "/garply/qux/grault", NULL); r3_tree_insert_path(n, "/garply/quux/foo", NULL); r3_tree_insert_path(n, "/garply/quux/bar", NULL); r3_tree_insert_path(n, "/garply/quux/baz", NULL); r3_tree_insert_path(n, "/garply/quux/qux", NULL); r3_tree_insert_path(n, "/garply/quux/corge", NULL); r3_tree_insert_path(n, "/garply/quux/grault", NULL); r3_tree_insert_path(n, "/garply/corge/foo", NULL); r3_tree_insert_path(n, "/garply/corge/bar", NULL); r3_tree_insert_path(n, "/garply/corge/baz", NULL); r3_tree_insert_path(n, "/garply/corge/qux", NULL); r3_tree_insert_path(n, "/garply/corge/quux", NULL); r3_tree_insert_path(n, "/garply/corge/grault", NULL); r3_tree_insert_path(n, "/garply/grault/foo", NULL); r3_tree_insert_path(n, "/garply/grault/bar", NULL); r3_tree_insert_path(n, "/garply/grault/baz", NULL); r3_tree_insert_path(n, "/garply/grault/qux", NULL); r3_tree_insert_path(n, "/garply/grault/quux", NULL); r3_tree_insert_path(n, "/garply/grault/corge", NULL); r3_tree_compile(n); // r3_tree_dump(n, 0); // match_entry *entry = calloc( sizeof(entry) , 1 ); node *m; m = r3_tree_match(n , "/qux/bar/corge", NULL); fail_if( m == NULL ); // r3_tree_dump( m, 0 ); ck_assert_int_eq( *((int*) m->data), 999 ); printf("Benchmarking...\n"); BENCHMARK(string_dispatch) r3_tree_matchl(n , "/qux/bar/corge", strlen("/qux/bar/corge"), NULL); END_BENCHMARK() bench_print_summary(&B); FILE *fp = fopen("bench_str.csv", "a+"); fprintf(fp, "%ld,%.2f\n", unixtime(), (B.N * B.R) / (B.end - B.start)); fclose(fp); }
END_TEST START_TEST (test_compile) { str_array *t; node * n; n = r3_tree_create(10); node *m; edge *e ; r3_tree_insert_path(n, "/zoo", NULL); r3_tree_insert_path(n, "/foo", NULL); r3_tree_insert_path(n, "/bar", NULL); r3_tree_compile(n); fail_if( n->combined_pattern ); fail_if( NULL == r3_node_find_edge_str(n, "/", strlen("/") ) ); #ifdef DEBUG r3_tree_dump(n, 0); #endif r3_tree_insert_path(n, "/foo/{id}", NULL); r3_tree_insert_path(n, "/{id}", NULL); r3_tree_compile(n); r3_tree_compile(n); // test double compile #ifdef DEBUG r3_tree_dump(n, 0); #endif /* fail_if(n->edges[0]->child->combined_pattern == NULL); e = r3_node_find_edge_str(n, "/", strlen("/") ); fail_if( NULL == e ); */ /* printf( "%s\n", e->pattern ); printf( "%s\n", e->child->combined_pattern ); printf( "%s\n", n->edges[0]->child->combined_pattern); printf( "%s\n", n->combined_pattern ); */ match_entry * entry; entry = match_entry_createl( "foo" , strlen("/foo") ); m = r3_tree_matchl( n , "/foo", strlen("/foo"), entry); fail_if( NULL == m ); entry = match_entry_createl( "/zoo" , strlen("/zoo") ); m = r3_tree_matchl( n , "/zoo", strlen("/zoo"), entry); fail_if( NULL == m ); entry = match_entry_createl( "/bar" , strlen("/bar") ); m = r3_tree_matchl( n , "/bar", strlen("/bar"), entry); fail_if( NULL == m ); entry = match_entry_createl( "/xxx" , strlen("/xxx") ); m = r3_tree_matchl( n , "/xxx", strlen("/xxx"), entry); fail_if( NULL == m ); entry = match_entry_createl( "/foo/xxx" , strlen("/foo/xxx") ); m = r3_tree_matchl( n , "/foo/xxx", strlen("/foo/xxx"), entry); fail_if( NULL == m ); entry = match_entry_createl( "/some_id" , strlen("/some_id") ); m = r3_tree_matchl( n , "/some_id", strlen("/some_id"), entry); fail_if( NULL == m ); ck_assert_int_gt( m->endpoint , 0 ); // should not be an endpoint }
/** * Return the last inserted node. */ node * r3_tree_insert_pathl_ex(node *tree, const char *path, int path_len, route * route, void * data, char **errstr) { node * n = tree; // common edge edge * e = NULL; // If there is no path to insert at the node, we just increase the mount // point on the node and append the route. if (path_len == 0) { tree->endpoint++; if (route) { route->data = data; r3_node_append_route(tree, route); } return tree; } /* length of common prefix */ int prefix_len = 0; char *err = NULL; e = r3_node_find_common_prefix(tree, path, path_len, &prefix_len, &err); if (err) { // copy the error message pointer if (errstr) *errstr = err; return NULL; } const char * subpath = path + prefix_len; const int subpath_len = path_len - prefix_len; // common prefix not found, insert a new edge for this pattern if ( prefix_len == 0 ) { // there are two more slugs, we should break them into several parts int slug_cnt = r3_slug_count(path, path_len, errstr); if (slug_cnt == -1) { return NULL; } if ( slug_cnt > 1 ) { int slug_len; char *p = r3_slug_find_placeholder(path, &slug_len); #ifdef DEBUG assert(p); #endif // find the next one '{', then break there if(p) { p = r3_slug_find_placeholder(p + slug_len + 1, NULL); } #ifdef DEBUG assert(p); #endif // insert the first one edge, and break at "p" node * child = r3_tree_create(3); CHECK_PTR(child); r3_node_connect(n, zstrndup(path, (int)(p - path)), child); // and insert the rest part to the child return r3_tree_insert_pathl_ex(child, p, path_len - (int)(p - path), route, data, errstr); } else { if (slug_cnt == 1) { // there is one slug, let's see if it's optimiz-able by opcode int slug_len = 0; char *slug_p = r3_slug_find_placeholder(path, &slug_len); int slug_pattern_len = 0; char *slug_pattern = r3_slug_find_pattern(slug_p, &slug_pattern_len); int opcode = 0; // if there is a pattern defined. if (slug_pattern_len) { char *cpattern = r3_slug_compile(slug_pattern, slug_pattern_len); opcode = r3_pattern_to_opcode(cpattern, strlen(cpattern)); zfree(cpattern); } else { opcode = OP_EXPECT_NOSLASH; } // if the slug starts after one+ charactor, for example foo{slug} node *c1; if (slug_p > path) { c1 = r3_tree_create(3); CHECK_PTR(c1); r3_node_connectl(n, path, slug_p - path, 1, c1); // duplicate } else { c1 = n; } node * c2 = r3_tree_create(3); CHECK_PTR(c2); edge * op_edge = r3_node_connectl(c1, slug_p, slug_len , 1, c2); if(opcode) { op_edge->opcode = opcode; } int restlen = path_len - ((slug_p - path) + slug_len); if (restlen) { return r3_tree_insert_pathl_ex(c2, slug_p + slug_len, restlen, route, data, errstr); } c2->data = data; c2->endpoint++; if (route) { route->data = data; r3_node_append_route(c2, route); } return c2; } // only one slug node * child = r3_tree_create(3); CHECK_PTR(child); child->endpoint++; if (data) child->data = data; r3_node_connectl(n, path, path_len, 1, child); if (route) { route->data = data; r3_node_append_route(child, route); } return child; } } else if ( prefix_len == e->pattern_len ) { // fully-equal to the pattern of the edge // there are something more we can insert if ( subpath_len > 0 ) { return r3_tree_insert_pathl_ex(e->child, subpath, subpath_len, route, data, errstr); } else { // there are no more path to insert // see if there is an endpoint already, we should n't overwrite the data on child. // but we still need to append the route. if (route) { route->data = data; r3_node_append_route(e->child, route); e->child->endpoint++; // make it as an endpoint return e->child; } // insertion without route if (e->child->endpoint > 0) { // TODO: return an error code instead of NULL return NULL; } e->child->endpoint++; // make it as an endpoint e->child->data = data; // set data return e->child; } } else if ( prefix_len < e->pattern_len ) { /* it's partially matched with the pattern, * we should split the end point and make a branch here... */ r3_edge_branch(e, prefix_len); return r3_tree_insert_pathl_ex(e->child, subpath, subpath_len, route , data, errstr); } else { fprintf(stderr, "unexpected route."); return NULL; } return n; }
/** * Return the last inserted node. */ node * r3_tree_insert_pathl_(node *tree, char *path, int path_len, route * route, void * data) { node * n = tree; edge * e = NULL; /* length of common prefix */ int prefix_len = 0; for( int i = 0 ; i < n->edge_len ; i++ ) { prefix_len = strndiff(path, n->edges[i]->pattern, n->edges[i]->pattern_len); // printf("prefix_len: %d %s vs %s\n", prefix_len, path, n->edges[i]->pattern ); // no common, consider insert a new edge if ( prefix_len > 0 ) { e = n->edges[i]; break; } } // branch the edge at correct position (avoid broken slugs) char *slug_s; if ( (slug_s = inside_slug(path, path_len, path + prefix_len)) != NULL ) { prefix_len = slug_s - path; } // common prefix not found, insert a new edge for this pattern if ( prefix_len == 0 ) { // there are two more slugs, we should break them into several parts int slug_cnt = slug_count(path, path_len); if ( slug_cnt > 1 ) { int slug_len; char *p = slug_find_placeholder(path, &slug_len); #ifdef DEBUG assert(p); #endif // find the next one '{', then break there if(p) { p = slug_find_placeholder(p + slug_len + 1, NULL); } #ifdef DEBUG assert(p); #endif // insert the first one edge, and break at "p" node * child = r3_tree_create(3); r3_node_connect(n, zstrndup(path, (int)(p - path)), child); // and insert the rest part to the child return r3_tree_insert_pathl_(child, p, path_len - (int)(p - path), route, data); } else { if (slug_cnt == 1) { // there is one slug, let's see if it's optimiz-able by opcode int slug_len = 0; char *slug_p = slug_find_placeholder(path, &slug_len); int slug_pattern_len = 0; char *slug_pattern = slug_find_pattern(slug_p, &slug_pattern_len); int opcode = 0; // if there is a pattern defined. if (slug_pattern) { char *cpattern = slug_compile(slug_pattern, slug_pattern_len); opcode = r3_pattern_to_opcode(cpattern, strlen(cpattern)); zfree(cpattern); } else { opcode = OP_EXPECT_NOSLASH; } // found opcode if (opcode) { // if the slug starts after one+ charactor, for example foo{slug} node *c1; if (slug_p > path) { c1 = r3_tree_create(3); r3_node_connectl(n, path, slug_p - path, 1, c1); // duplicate } else { c1 = n; } node * c2 = r3_tree_create(3); edge * op_edge = r3_node_connectl(c1, slug_p, slug_len , 1, c2); op_edge->opcode = opcode; // insert rest int restlen = (path_len - (slug_p - path)) - slug_len; if (restlen) { return r3_tree_insert_pathl_(c2, slug_p + slug_len, restlen, route, data); } c2->data = data; c2->endpoint++; if (route) { route->data = data; r3_node_append_route(c2, route); } return c2; } } // only one slug node * child = r3_tree_create(3); r3_node_connect(n, zstrndup(path, path_len) , child); child->data = data; child->endpoint++; if (route) { route->data = data; r3_node_append_route(child, route); } return child; } } else if ( prefix_len == e->pattern_len ) { // fully-equal to the pattern of the edge char * subpath = path + prefix_len; int subpath_len = path_len - prefix_len; // there are something more we can insert if ( subpath_len > 0 ) { return r3_tree_insert_pathl_(e->child, subpath, subpath_len, route, data); } else { // there are no more path to insert // see if there is an endpoint already if (e->child->endpoint > 0) { // XXX: return an error code instead of NULL return NULL; } e->child->endpoint++; // make it as an endpoint e->child->data = data; if (route) { route->data = data; r3_node_append_route(e->child, route); } return e->child; } } else if ( prefix_len < e->pattern_len ) { /* it's partially matched with the pattern, * we should split the end point and make a branch here... */ char * s2 = path + prefix_len; int s2_len = path_len - prefix_len; r3_edge_branch(e, prefix_len); return r3_tree_insert_pathl_(e->child, s2 , s2_len, route , data); } else { printf("unexpected route."); return NULL; } return n; }
/** * Return the last inserted node. */ node * r3_tree_insert_pathl(node *tree, char *path, int path_len, route * route, void * data) { node * n = tree; edge * e = NULL; /* length of common prefix */ int offset = 0; for( int i = 0 ; i < n->edge_len ; i++ ) { offset = strndiff(path, n->edges[i]->pattern, n->edges[i]->pattern_len); // printf("offset: %d %s vs %s\n", offset, path, n->edges[i]->pattern ); // no common, consider insert a new edge if ( offset > 0 ) { e = n->edges[i]; break; } } // branch the edge at correct position (avoid broken slugs) char *slug_s = strchr(path, '{'); char *slug_e = strchr(path, '}'); if ( slug_s && slug_e ) { if ( offset > (slug_s - path) && offset < (slug_e - path) ) { // break before '{' offset = slug_s - path; } } if ( offset == 0 ) { // not found, we should just insert a whole new edge node * child = r3_tree_create(3); r3_node_add_child(n, strndup(path, path_len) , child); info("edge not found, insert one: %s\n", path); child->data = data; child->endpoint++; if (route) { route->data = data; r3_node_append_route(child, route); } return child; } else if ( offset == e->pattern_len ) { // fully-equal to the pattern of the edge char * subpath = path + offset; int subpath_len = path_len - offset; // there are something more we can insert if ( subpath_len > 0 ) { return r3_tree_insert_pathl(e->child, subpath, subpath_len, route, data); } else { // no more path to insert e->child->endpoint++; // make it as an endpoint e->child->data = data; if (route) { route->data = data; r3_node_append_route(e->child, route); } return e->child; } } else if ( offset < e->pattern_len ) { // printf("branch the edge offset: %d\n", offset); /* it's partially matched with the pattern, * we should split the end point and make a branch here... */ node *c2; // child 1, child 2 edge *e2; // edge 1, edge 2 char * s2 = path + offset; int s2_len = 0; r3_edge_branch(e, offset); // here is the new edge from. c2 = r3_tree_create(3); s2_len = path_len - offset; e2 = r3_edge_create(strndup(s2, s2_len), s2_len, c2); // printf("edge right: %s\n", e2->pattern); r3_node_append_edge(e->child, e2); char *op = e->pattern; // truncate the original edge pattern e->pattern = strndup(e->pattern, offset); e->pattern_len = offset; free(op); // move n->edges to c1 c2->endpoint++; c2->data = data; if (route) { route->data = data; r3_node_append_route(c2, route); } return c2; } else { printf("unexpected route."); return NULL; } return n; }