/** * 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_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; }