diff --git a/examples/routing.c b/examples/routing.c new file mode 100644 index 0000000..6d83c01 --- /dev/null +++ b/examples/routing.c @@ -0,0 +1,78 @@ +/* + * check_slug.c + * Copyright (C) 2014 c9s + * + * Distributed under terms of the MIT license. + */ +#include +#include +#include "../include/r3.h" + + + + +void test1(void) { + R3Node *n = r3_tree_create(10); + + int route_data = 3; + + // insert the R3Route path into the router tree + r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog", sizeof("/blog") - 1, &route_data ); + r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog/{post}/asf/{id}", sizeof("/blog/{post}/asf/{id}") - 1, &route_data ); + r3_tree_insert_routel(n, METHOD_GET | METHOD_POST, "/blog/{post}/asd/{id:[0-9]+}/qwe", sizeof("/blog/{post}/asd/{id:[0-9]+}/qwe") - 1, &route_data ); + + char *errstr = NULL; + int err = r3_tree_compile(n, &errstr); + if (err != 0) { + // fail + printf("error: %s\n", errstr); + free(errstr); // errstr is created from `asprintf`, so you have to free it manually. + } + + + // in your http server handler + + // create the match entry for capturing dynamic variables. + match_entry * entry; + R3Route *matched_route; + // for (int k = 0; k < 3000000; k++) { + entry = match_entry_create("/blog"); + if (entry != NULL) { + entry->request_method = METHOD_GET; + matched_route = r3_tree_match_route(n, entry); + if (matched_route != NULL) { + // printf("Routed data is: %d\n", *(int*)matched_route->data); // get the data from matched route + for (int i = 0; i < entry->vars->len; i++) { + entry->vars->slugs[i]; + entry->vars->tokens[i]; + // printf("Slug name is: %s\n",entry->vars->slugs[i]); + // printf("Slug value is: %s\n",entry->vars->tokens[i]); + } + } + } + // free the objects at the end + match_entry_free(entry); + // } + entry = match_entry_create("/blog/aaa/asd/123/qwe"); + if (entry != NULL) { + entry->request_method = METHOD_GET; + matched_route = r3_tree_match_route(n, entry); + if (matched_route != NULL) { + // printf("Routed data is: %d\n", *(int*)matched_route->data); // get the data from matched route + for (int i = 0; i < entry->vars->len; i++) { + // entry->vars->slugs[i]; + // entry->vars->tokens[i]; + printf("Slug name is: %s\n",entry->vars->slugs[i]); + printf("Slug value is: %s\n",entry->vars->tokens[i]); + } + } + } + // free the objects at the end + match_entry_free(entry); + + r3_tree_free(n); +} + +int main (int argc, char *argv[]) { + test1(); +} diff --git a/examples/simple.c b/examples/simple.c index 8c37650..342e3e1 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -54,5 +54,6 @@ int main() printf("Matched! %s\n", e->path); } match_entry_free(e); + r3_tree_free(n); return 0; } diff --git a/include/r3.h b/include/r3.h index ea727cb..d7929c8 100644 --- a/include/r3.h +++ b/include/r3.h @@ -46,7 +46,7 @@ typedef struct _node R3Node; typedef struct _R3Route R3Route; struct _node { - R3Edge * edges; + R3Edge ** edges; char * combined_pattern; pcre * pcre_pattern; pcre_extra * pcre_extra; @@ -83,6 +83,10 @@ struct _R3Route { char * path; int path_len; + char **slugs; + int slugs_len; + int slugs_cap; + int request_method; // can be (GET || POST) char * host; // required host name diff --git a/include/str_array.h b/include/str_array.h index 0eca323..f4490dd 100644 --- a/include/str_array.h +++ b/include/str_array.h @@ -9,6 +9,8 @@ #define STR_ARRAY_H typedef struct _str_array { + char **slugs; + int slugs_len; char **tokens; int len; int cap; @@ -16,14 +18,20 @@ typedef struct _str_array { str_array * str_array_create(int cap); -bool str_array_is_full(const str_array * l); +bool str_array_slugs_full(const str_array * l); + +bool str_array_tokens_full(const str_array * l); bool str_array_resize(str_array *l, int new_cap); +bool str_array_append_slug(str_array * l, char * slug); + bool str_array_append(str_array * list, char * token); void str_array_free(str_array *l); +void str_array_dump_slugs(const str_array *l); + void str_array_dump(const str_array *l); str_array * split_route_pattern(char *pattern, int pattern_len); diff --git a/src/edge.c b/src/edge.c index da06192..466c154 100644 --- a/src/edge.c +++ b/src/edge.c @@ -86,11 +86,13 @@ R3Node * r3_edge_branch(R3Edge *e, int dl) { } void r3_edge_free(R3Edge * e) { - zfree(e->pattern); - if ( e->child ) { - r3_tree_free(e->child); + if (e) { + zfree(e->pattern); + if ( e->child ) { + r3_tree_free(e->child); + } + // free itself + zfree(e); } - // free itself - zfree(e); } diff --git a/src/node.c b/src/node.c index 0d01a52..8bbbde7 100644 --- a/src/node.c +++ b/src/node.c @@ -56,7 +56,7 @@ R3Node * r3_tree_create(int cap) { R3Node * n = (R3Node*) zmalloc( sizeof(R3Node) ); CHECK_PTR(n); - n->edges = (R3Edge*) zmalloc(sizeof(R3Edge) * cap); + n->edges = zmalloc(sizeof(R3Edge) * cap); CHECK_PTR(n->edges); n->edge_len = 0; n->edge_cap = cap; @@ -66,6 +66,7 @@ R3Node * r3_tree_create(int cap) { n->route_cap = 0; n->endpoint = 0; + n->compare_type = NODE_COMPARE_STR; n->combined_pattern = NULL; n->pcre_pattern = NULL; n->pcre_extra = NULL; @@ -75,9 +76,17 @@ R3Node * r3_tree_create(int cap) { void r3_tree_free(R3Node * tree) { if (tree->edges) { + for (int j=0;jedge_len;j++) { + r3_edge_free(tree->edges[j]); + } zfree(tree->edges); } - zfree(tree->routes); + if (tree->routes) { + for (int k=0;kroute_len;k++) { + r3_route_free(tree->routes[k]); + } + zfree(tree->routes); + } if (tree->pcre_pattern) { pcre_free(tree->pcre_pattern); } @@ -110,8 +119,7 @@ R3Edge * r3_node_connectl(R3Node * n, const char * pat, int len, int dupl, R3Nod } e = r3_edge_createl(pat, len, child); CHECK_PTR(e); - R3Edge *e2 = r3_node_append_edge(n, e); - zfree(e); + R3Edge * e2 = r3_node_append_edge(n, e); return e2; } @@ -129,10 +137,9 @@ R3Edge * r3_node_append_edge(R3Node *n, R3Edge *e) } } - // r3_edge_initl( - // copy 'edge' into the edge array - n->edges[n->edge_len] = *e; - return &n->edges[n->edge_len++]; + // Insert edge into edge array + n->edges[n->edge_len] = e; + return n->edges[n->edge_len++]; } @@ -145,7 +152,7 @@ R3Edge * r3_node_find_edge(const R3Node * n, const char * pat, int pat_len) { R3Edge * e; int i; for (i = 0 ; i < n->edge_len ; i++ ) { - e = &n->edges[i]; + e = n->edges[i]; // there is a case: "{foo}" vs "{foo:xxx}", // we should return the match result: full-match or partial-match if (strcmp(e->pattern, pat) == 0) { @@ -170,7 +177,7 @@ int r3_tree_compile(R3Node *n, char **errstr) } for (i = 0 ; i < n->edge_len ; i++ ) { - if ((ret = r3_tree_compile(n->edges[i].child, errstr))) { + if ((ret = r3_tree_compile(n->edges[i]->child, errstr))) { return ret; // stop here if error occurs } } @@ -197,7 +204,7 @@ int r3_tree_compile_patterns(R3Node * n, char **errstr) { int opcode_cnt = 0; int i = 0; for (; i < n->edge_len ; i++) { - e = &n->edges[i]; + e = n->edges[i]; if (e->opcode) { opcode_cnt++; } @@ -206,6 +213,7 @@ int r3_tree_compile_patterns(R3Node * n, char **errstr) { // compile "foo/{slug}" to "foo/[^/]+" char * slug_pat = r3_slug_compile(e->pattern, e->pattern_len); strcat(p, slug_pat); + zfree(slug_pat); } else { strncat(p,"^(", 2); p += 2; @@ -298,7 +306,7 @@ R3Node * r3_tree_matchl(const R3Node * n, const char * path, int path_len, match for (i = n->edge_len; i--; ) { pp = path; - e = &n->edges[i]; + e = n->edges[i]; switch(e->opcode) { case OP_EXPECT_NOSLASH: while (*pp != '/' && pp < pp_end) pp++; @@ -384,7 +392,7 @@ R3Node * r3_tree_matchl(const R3Node * n, const char * path, int path_len, match continue; substring_start = path + ov[2*i]; - e = &n->edges[i - 1]; + e = n->edges[i - 1]; if (entry && e->has_slug) { // append captured token to entry @@ -408,7 +416,7 @@ R3Node * r3_tree_matchl(const R3Node * n, const char * path, int path_len, match } substring_start = path + ov[2*i]; - e = &n->edges[i - 1]; + e = n->edges[i - 1]; if (entry && e->has_slug) { // append captured token to entry @@ -441,6 +449,8 @@ R3Route * r3_tree_match_route(const R3Node *tree, match_entry * entry) { if (n && n->routes && n->route_len > 0) { for (i = n->route_len; i--; ) { if ( r3_route_cmp(n->routes[i], entry) == 0 ) { + // Add slugs from found route to match_entry + entry->vars->slugs = n->routes[i]->slugs; return n->routes[i]; } } @@ -453,10 +463,10 @@ inline R3Edge * r3_node_find_edge_str(const R3Node * n, const char * str, int st unsigned int i; char firstbyte = *str; for (i = n->edge_len; i--; ) { - e = &n->edges[i]; + e = n->edges[i]; if (firstbyte == e->pattern[0]) { if (strncmp(e->pattern, str, e->pattern_len) == 0) { - return &n->edges[i]; + return n->edges[i]; } return NULL; } @@ -484,12 +494,59 @@ R3Node * r3_node_create() { } void r3_route_free(R3Route * route) { + assert(route); + for ( int i = 0; i < route->slugs_len ; i++ ) { + if (route->slugs[ i ]) { + zfree(route->slugs[i]); + } + } + zfree(route->slugs); zfree(route); } +static bool router_slugs_full(const R3Route * route) { + return route->slugs_len >= route->slugs_cap; +} + +static bool router_slugs_resize(R3Route * route, int new_cap) { + route->slugs = zrealloc(route->slugs, sizeof(char**) * new_cap); + route->slugs_cap = new_cap; + return route->slugs != NULL; +} + +static bool router_append_slug(R3Route * route, char * slug) { + if ( router_slugs_full(route) ) { + bool ret = router_slugs_resize(route, route->slugs_cap + 20); + if (ret == false ) { + return false; + } + } + route->slugs[ route->slugs_len++ ] = slug; + return true; +} + +static void get_slugs(const char * path, int path_len, R3Route * route) { + char *plh = path; + int l = 0; + int namel; + char *name; + while(1) { + plh = r3_slug_find_placeholder(plh+l,&l); + if (plh == NULL) break; + namel = 0; + name = r3_slug_find_name(plh,&namel); + router_append_slug(route,zstrndup(name,namel)); + if ((plh + l) >= (path + path_len)) break; + } +} + R3Route * r3_route_createl(const char * path, int path_len) { R3Route * info = zmalloc(sizeof(R3Route)); CHECK_PTR(info); + info->slugs_cap = 3; + info->slugs_len = 0; + info->slugs = (char**) zmalloc( sizeof(char*) * info->slugs_cap); + get_slugs(path, path_len, info); info->path = (char*) path; info->path_len = path_len; info->request_method = 0; // can be (GET || POST) @@ -544,11 +601,11 @@ R3Edge * r3_node_find_common_prefix(R3Node *n, const char *path, int path_len, i R3Edge *e = NULL; for(i = 0 ; i < n->edge_len ; i++ ) { // ignore all edges with slug - prefix = strndiff( (char*) path, n->edges[i].pattern, n->edges[i].pattern_len); + prefix = strndiff( (char*) path, n->edges[i]->pattern, n->edges[i]->pattern_len); // no common, consider insert a new edge if ( prefix > 0 ) { - e = &n->edges[i]; + e = n->edges[i]; break; } } @@ -660,7 +717,7 @@ R3Node * r3_tree_insert_pathl_ex(R3Node *tree, const char *path, int path_len, R R3Node * child = r3_tree_create(3); CHECK_PTR(child); - r3_node_connect(n, zstrndup(path, (int)(p - path)), child); + r3_node_connectl(n, path, p - path, 1, child); // duplicate // and insert the rest part to the child return r3_tree_insert_pathl_ex(child, p, path_len - (int)(p - path), route, data, errstr); @@ -775,7 +832,7 @@ bool r3_node_has_slug_edges(const R3Node *n) { bool found = false; R3Edge *e; for ( int i = 0 ; i < n->edge_len ; i++ ) { - e = &n->edges[i]; + e = n->edges[i]; e->has_slug = r3_path_contains_slug_char(e->pattern); if (e->has_slug) found = true; @@ -802,7 +859,7 @@ void r3_tree_dump(const R3Node * n, int level) { printf("\n"); for ( int i = 0 ; i < n->edge_len ; i++ ) { - R3Edge * e = &n->edges[i]; + R3Edge * e = n->edges[i]; print_indent(level + 1); printf("|-\"%s\"", e->pattern); diff --git a/src/token.c b/src/token.c index d59abe1..0cefd88 100644 --- a/src/token.c +++ b/src/token.c @@ -19,7 +19,9 @@ str_array * str_array_create(int cap) { if (!list) return NULL; list->len = 0; + list->slugs_len = 0; list->cap = cap; + list->slugs = NULL; list->tokens = (char**) zmalloc( sizeof(char*) * cap); return list; } @@ -35,18 +37,34 @@ void str_array_free(str_array *l) { zfree(l); } -bool str_array_is_full(const str_array * l) { +bool str_array_slugs_full(const str_array * l) { + return l->slugs_len >= l->cap; +} + +bool str_array_tokens_full(const str_array * l) { return l->len >= l->cap; } bool str_array_resize(str_array * l, int new_cap) { + l->slugs = zrealloc(l->slugs, sizeof(char**) * new_cap); l->tokens = zrealloc(l->tokens, sizeof(char**) * new_cap); l->cap = new_cap; - return l->tokens != NULL; + return l->tokens != NULL && l->slugs != NULL; +} + +bool str_array_append_slug(str_array * l, char * slug) { + if ( str_array_slugs_full(l) ) { + bool ret = str_array_resize(l, l->cap + 20); + if (ret == false ) { + return false; + } + } + l->slugs[ l->slugs_len++ ] = slug; + return true; } bool str_array_append(str_array * l, char * token) { - if ( str_array_is_full(l) ) { + if ( str_array_tokens_full(l) ) { bool ret = str_array_resize(l, l->cap + 20); if (ret == false ) { return false; @@ -56,6 +74,17 @@ bool str_array_append(str_array * l, char * token) { return true; } +void str_array_dump_slugs(const str_array *l) { + printf("["); + for ( int i = 0; i < l->slugs_len ; i++ ) { + printf("\"%s\"", l->slugs[i] ); + if ( i + 1 != l->slugs_len ) { + printf(", "); + } + } + printf("]\n"); +} + void str_array_dump(const str_array *l) { printf("["); for ( int i = 0; i < l->len ; i++ ) {