Compare commits
3 commits
2.0
...
v2/slug-pa
Author | SHA1 | Date | |
---|---|---|---|
|
5e01820f14 | ||
|
43fd09e8a6 | ||
|
0c5f1f6b5c |
10 changed files with 217 additions and 37 deletions
|
@ -29,7 +29,7 @@ script:
|
|||
- ./configure --enable-check $CONFIGURE_OPTION
|
||||
- make V=1
|
||||
- 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...
|
||||
# - if [ "x$VALGRIND" == xyes && "x$DEBUG" == xyes ]; then valgrind ./tests/check_* -v --trace-children=yes --show-leak-kinds=full --leak-check=full; fi
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
SUBDIRS=3rdparty src . examples
|
||||
|
||||
if HAVE_CHECK
|
||||
if ENABLE_CHECK
|
||||
SUBDIRS += tests
|
||||
endif
|
||||
|
||||
|
|
|
@ -118,15 +118,16 @@ AC_ARG_ENABLE(check,
|
|||
AS_HELP_STRING([--enable-check],
|
||||
[enable unit testing]),
|
||||
, enable_check=unset)
|
||||
|
||||
if test "x$enable_check" != "xunset" ; then
|
||||
PKG_CHECK_MODULES(CHECK,[check >= 0.9.4],:,[
|
||||
ifdef([AM_PATH_CHECK],
|
||||
[AM_PATH_CHECK(,[have_check="yes"])],
|
||||
AC_MSG_WARN([Check not found; cannot run unit tests!])
|
||||
[have_check="no"])]
|
||||
AC_MSG_WARN([Check not found; cannot run unit tests!]) [have_check="no"]
|
||||
)]
|
||||
])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_CHECK, test x"$have_check" = "xyes")
|
||||
AM_CONDITIONAL(ENABLE_CHECK, test "x$enable_check" = "xyes")
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
r3.pc
|
||||
|
|
78
examples/routing.c
Normal file
78
examples/routing.c
Normal 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();
|
||||
}
|
|
@ -54,5 +54,6 @@ int main()
|
|||
printf("Matched! %s\n", e->path);
|
||||
}
|
||||
match_entry_free(e);
|
||||
r3_tree_free(n);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -86,6 +86,7 @@ R3Node * r3_edge_branch(R3Edge *e, int dl) {
|
|||
}
|
||||
|
||||
void r3_edge_free(R3Edge * e) {
|
||||
if (e) {
|
||||
zfree(e->pattern);
|
||||
if ( e->child ) {
|
||||
r3_tree_free(e->child);
|
||||
|
@ -93,4 +94,5 @@ void r3_edge_free(R3Edge * e) {
|
|||
// free itself
|
||||
zfree(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
95
src/node.c
95
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;j<tree->edge_len;j++) {
|
||||
r3_edge_free(tree->edges[j]);
|
||||
}
|
||||
zfree(tree->edges);
|
||||
}
|
||||
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) {
|
||||
pcre_free(tree->pcre_pattern);
|
||||
}
|
||||
|
@ -111,7 +120,6 @@ 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);
|
||||
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);
|
||||
|
||||
|
|
35
src/token.c
35
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++ ) {
|
||||
|
|
Loading…
Reference in a new issue