/** * 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; // the suffix edge of the leaf new_child = r3_tree_create(3); s1_len = e->pattern_len - dl; e1 = r3_edge_createl(zstrndup(s1, s1_len), s1_len, new_child); // Migrate the child edges to the new edge we just created. for ( int i = 0 ; i < e->child->edge_len ; i++ ) { r3_node_append_edge(new_child, e->child->edges[i]); e->child->edges[i] = NULL; } e->child->edge_len = 0; // Migrate the child routes for ( int i = 0 ; i < e->child->route_len ; i++ ) { r3_node_append_route(new_child, e->child->routes[i]); e->child->routes[i] = NULL; } e->child->route_len = 0; // Migrate the endpoint new_child->endpoint = e->child->endpoint; e->child->endpoint = 0; // reset endpoint // Migrate the data new_child->data = e->child->data; // copy data pointer e->child->data = NULL; r3_node_append_edge(e->child, e1); // truncate the original edge pattern char *oldpattern = e->pattern; e->pattern = zstrndup(e->pattern, dl); e->pattern_len = dl; zfree(oldpattern); return new_child; }
/** * 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; }
/** * 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; }
/** * 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; }