Compare commits

...

3 commits

Author SHA1 Message Date
karantin2020 5e01820f14 Added slug parser and repaired few memory leaks
- Added routing example
- Changed slug parsing
- Changed routing example
- Clean heap after use in simple example

Pull Request:

- https://github.com/c9s/r3/pull/95
2016-03-12 12:41:24 +08:00
c9s 43fd09e8a6 Check is not running, fix check 2016-03-12 12:40:21 +08:00
Yo-An Lin 0c5f1f6b5c Update .travis.yml 2016-03-12 12:40:21 +08:00
10 changed files with 217 additions and 37 deletions

View file

@ -29,7 +29,7 @@ script:
- ./configure --enable-check $CONFIGURE_OPTION - ./configure --enable-check $CONFIGURE_OPTION
- make V=1 - make V=1
- sudo make install - sudo make install
- if [ "x$VALGRIND" == xyes ]; then make check > /dev/null 2>&1; else make check V=1; fi - if [ "x$VALGRIND" == xyes ]; then make check ; else make check V=1; fi
# XXX: tracing memory leak, disabled for some mystery reason for automake... # XXX: tracing memory leak, disabled for some mystery reason for automake...
# - if [ "x$VALGRIND" == xyes && "x$DEBUG" == xyes ]; then valgrind ./tests/check_* -v --trace-children=yes --show-leak-kinds=full --leak-check=full; fi # - if [ "x$VALGRIND" == xyes && "x$DEBUG" == xyes ]; then valgrind ./tests/check_* -v --trace-children=yes --show-leak-kinds=full --leak-check=full; fi

View file

@ -1,6 +1,6 @@
SUBDIRS=3rdparty src . examples SUBDIRS=3rdparty src . examples
if HAVE_CHECK if ENABLE_CHECK
SUBDIRS += tests SUBDIRS += tests
endif endif

View file

@ -118,15 +118,16 @@ AC_ARG_ENABLE(check,
AS_HELP_STRING([--enable-check], AS_HELP_STRING([--enable-check],
[enable unit testing]), [enable unit testing]),
, enable_check=unset) , enable_check=unset)
if test "x$enable_check" != "xunset" ; then if test "x$enable_check" != "xunset" ; then
PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[ PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[
ifdef([AM_PATH_CHECK], ifdef([AM_PATH_CHECK],
[AM_PATH_CHECK(,[have_check="yes"])], [AM_PATH_CHECK(,[have_check="yes"])],
AC_MSG_WARN([Check not found; cannot run unit tests!]) AC_MSG_WARN([Check not found; cannot run unit tests!]) [have_check="no"]
[have_check="no"])] )]
]) ])
fi fi
AM_CONDITIONAL(HAVE_CHECK, test x"$have_check" = "xyes") AM_CONDITIONAL(ENABLE_CHECK, test "x$enable_check" = "xyes")
AC_CONFIG_FILES([ AC_CONFIG_FILES([
r3.pc r3.pc

78
examples/routing.c Normal file
View file

@ -0,0 +1,78 @@
/*
* check_slug.c
* Copyright (C) 2014 c9s <yoanlin93@gmail.com>
*
* Distributed under terms of the MIT license.
*/
#include <stdio.h>
#include <stdlib.h>
#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();
}

View file

@ -54,5 +54,6 @@ int main()
printf("Matched! %s\n", e->path); printf("Matched! %s\n", e->path);
} }
match_entry_free(e); match_entry_free(e);
r3_tree_free(n);
return 0; return 0;
} }

View file

@ -46,7 +46,7 @@ typedef struct _node R3Node;
typedef struct _R3Route R3Route; typedef struct _R3Route R3Route;
struct _node { struct _node {
R3Edge * edges; R3Edge ** edges;
char * combined_pattern; char * combined_pattern;
pcre * pcre_pattern; pcre * pcre_pattern;
pcre_extra * pcre_extra; pcre_extra * pcre_extra;
@ -83,6 +83,10 @@ struct _R3Route {
char * path; char * path;
int path_len; int path_len;
char **slugs;
int slugs_len;
int slugs_cap;
int request_method; // can be (GET || POST) int request_method; // can be (GET || POST)
char * host; // required host name char * host; // required host name

View file

@ -9,6 +9,8 @@
#define STR_ARRAY_H #define STR_ARRAY_H
typedef struct _str_array { typedef struct _str_array {
char **slugs;
int slugs_len;
char **tokens; char **tokens;
int len; int len;
int cap; int cap;
@ -16,14 +18,20 @@ typedef struct _str_array {
str_array * str_array_create(int cap); 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_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); bool str_array_append(str_array * list, char * token);
void str_array_free(str_array *l); void str_array_free(str_array *l);
void str_array_dump_slugs(const str_array *l);
void str_array_dump(const str_array *l); void str_array_dump(const str_array *l);
str_array * split_route_pattern(char *pattern, int pattern_len); str_array * split_route_pattern(char *pattern, int pattern_len);

View file

@ -86,11 +86,13 @@ R3Node * r3_edge_branch(R3Edge *e, int dl) {
} }
void r3_edge_free(R3Edge * e) { void r3_edge_free(R3Edge * e) {
zfree(e->pattern); if (e) {
if ( e->child ) { zfree(e->pattern);
r3_tree_free(e->child); if ( e->child ) {
r3_tree_free(e->child);
}
// free itself
zfree(e);
} }
// free itself
zfree(e);
} }

View file

@ -56,7 +56,7 @@ R3Node * r3_tree_create(int cap) {
R3Node * n = (R3Node*) zmalloc( sizeof(R3Node) ); R3Node * n = (R3Node*) zmalloc( sizeof(R3Node) );
CHECK_PTR(n); CHECK_PTR(n);
n->edges = (R3Edge*) zmalloc(sizeof(R3Edge) * cap); n->edges = zmalloc(sizeof(R3Edge) * cap);
CHECK_PTR(n->edges); CHECK_PTR(n->edges);
n->edge_len = 0; n->edge_len = 0;
n->edge_cap = cap; n->edge_cap = cap;
@ -66,6 +66,7 @@ R3Node * r3_tree_create(int cap) {
n->route_cap = 0; n->route_cap = 0;
n->endpoint = 0; n->endpoint = 0;
n->compare_type = NODE_COMPARE_STR;
n->combined_pattern = NULL; n->combined_pattern = NULL;
n->pcre_pattern = NULL; n->pcre_pattern = NULL;
n->pcre_extra = NULL; n->pcre_extra = NULL;
@ -75,9 +76,17 @@ R3Node * r3_tree_create(int cap) {
void r3_tree_free(R3Node * tree) { void r3_tree_free(R3Node * tree) {
if (tree->edges) { if (tree->edges) {
for (int j=0;j<tree->edge_len;j++) {
r3_edge_free(tree->edges[j]);
}
zfree(tree->edges); zfree(tree->edges);
} }
zfree(tree->routes); if (tree->routes) {
for (int k=0;k<tree->route_len;k++) {
r3_route_free(tree->routes[k]);
}
zfree(tree->routes);
}
if (tree->pcre_pattern) { if (tree->pcre_pattern) {
pcre_free(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); e = r3_edge_createl(pat, len, child);
CHECK_PTR(e); CHECK_PTR(e);
R3Edge *e2 = r3_node_append_edge(n, e); R3Edge * e2 = r3_node_append_edge(n, e);
zfree(e);
return e2; return e2;
} }
@ -129,10 +137,9 @@ R3Edge * r3_node_append_edge(R3Node *n, R3Edge *e)
} }
} }
// r3_edge_initl( // Insert edge into edge array
// copy 'edge' into the edge array n->edges[n->edge_len] = e;
n->edges[n->edge_len] = *e; return n->edges[n->edge_len++];
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; R3Edge * e;
int i; int i;
for (i = 0 ; i < n->edge_len ; 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}", // there is a case: "{foo}" vs "{foo:xxx}",
// we should return the match result: full-match or partial-match // we should return the match result: full-match or partial-match
if (strcmp(e->pattern, pat) == 0) { 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++ ) { 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 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 opcode_cnt = 0;
int i = 0; int i = 0;
for (; i < n->edge_len ; i++) { for (; i < n->edge_len ; i++) {
e = &n->edges[i]; e = n->edges[i];
if (e->opcode) { if (e->opcode) {
opcode_cnt++; opcode_cnt++;
} }
@ -206,6 +213,7 @@ int r3_tree_compile_patterns(R3Node * n, char **errstr) {
// compile "foo/{slug}" to "foo/[^/]+" // compile "foo/{slug}" to "foo/[^/]+"
char * slug_pat = r3_slug_compile(e->pattern, e->pattern_len); char * slug_pat = r3_slug_compile(e->pattern, e->pattern_len);
strcat(p, slug_pat); strcat(p, slug_pat);
zfree(slug_pat);
} else { } else {
strncat(p,"^(", 2); strncat(p,"^(", 2);
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--; ) { for (i = n->edge_len; i--; ) {
pp = path; pp = path;
e = &n->edges[i]; e = n->edges[i];
switch(e->opcode) { switch(e->opcode) {
case OP_EXPECT_NOSLASH: case OP_EXPECT_NOSLASH:
while (*pp != '/' && pp < pp_end) pp++; 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; continue;
substring_start = path + ov[2*i]; substring_start = path + ov[2*i];
e = &n->edges[i - 1]; e = n->edges[i - 1];
if (entry && e->has_slug) { if (entry && e->has_slug) {
// append captured token to entry // 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]; substring_start = path + ov[2*i];
e = &n->edges[i - 1]; e = n->edges[i - 1];
if (entry && e->has_slug) { if (entry && e->has_slug) {
// append captured token to entry // 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) { if (n && n->routes && n->route_len > 0) {
for (i = n->route_len; i--; ) { for (i = n->route_len; i--; ) {
if ( r3_route_cmp(n->routes[i], entry) == 0 ) { 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]; 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; unsigned int i;
char firstbyte = *str; char firstbyte = *str;
for (i = n->edge_len; i--; ) { for (i = n->edge_len; i--; ) {
e = &n->edges[i]; e = n->edges[i];
if (firstbyte == e->pattern[0]) { if (firstbyte == e->pattern[0]) {
if (strncmp(e->pattern, str, e->pattern_len) == 0) { if (strncmp(e->pattern, str, e->pattern_len) == 0) {
return &n->edges[i]; return n->edges[i];
} }
return NULL; return NULL;
} }
@ -484,12 +494,59 @@ R3Node * r3_node_create() {
} }
void r3_route_free(R3Route * route) { 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); 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 * r3_route_createl(const char * path, int path_len) {
R3Route * info = zmalloc(sizeof(R3Route)); R3Route * info = zmalloc(sizeof(R3Route));
CHECK_PTR(info); 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 = (char*) path;
info->path_len = path_len; info->path_len = path_len;
info->request_method = 0; // can be (GET || POST) 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; R3Edge *e = NULL;
for(i = 0 ; i < n->edge_len ; i++ ) { for(i = 0 ; i < n->edge_len ; i++ ) {
// ignore all edges with slug // 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 // no common, consider insert a new edge
if ( prefix > 0 ) { if ( prefix > 0 ) {
e = &n->edges[i]; e = n->edges[i];
break; 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); R3Node * child = r3_tree_create(3);
CHECK_PTR(child); 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 // and insert the rest part to the child
return r3_tree_insert_pathl_ex(child, p, path_len - (int)(p - path), route, data, errstr); 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; bool found = false;
R3Edge *e; R3Edge *e;
for ( int i = 0 ; i < n->edge_len ; i++ ) { 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); e->has_slug = r3_path_contains_slug_char(e->pattern);
if (e->has_slug) if (e->has_slug)
found = true; found = true;
@ -802,7 +859,7 @@ void r3_tree_dump(const R3Node * n, int level) {
printf("\n"); printf("\n");
for ( int i = 0 ; i < n->edge_len ; i++ ) { for ( int i = 0 ; i < n->edge_len ; i++ ) {
R3Edge * e = &n->edges[i]; R3Edge * e = n->edges[i];
print_indent(level + 1); print_indent(level + 1);
printf("|-\"%s\"", e->pattern); printf("|-\"%s\"", e->pattern);

View file

@ -19,7 +19,9 @@ str_array * str_array_create(int cap) {
if (!list) if (!list)
return NULL; return NULL;
list->len = 0; list->len = 0;
list->slugs_len = 0;
list->cap = cap; list->cap = cap;
list->slugs = NULL;
list->tokens = (char**) zmalloc( sizeof(char*) * cap); list->tokens = (char**) zmalloc( sizeof(char*) * cap);
return list; return list;
} }
@ -35,18 +37,34 @@ void str_array_free(str_array *l) {
zfree(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; return l->len >= l->cap;
} }
bool str_array_resize(str_array * l, int new_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->tokens = zrealloc(l->tokens, sizeof(char**) * new_cap);
l->cap = 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) { 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); bool ret = str_array_resize(l, l->cap + 20);
if (ret == false ) { if (ret == false ) {
return false; return false;
@ -56,6 +74,17 @@ bool str_array_append(str_array * l, char * token) {
return true; 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) { void str_array_dump(const str_array *l) {
printf("["); printf("[");
for ( int i = 0; i < l->len ; i++ ) { for ( int i = 0; i < l->len ; i++ ) {