feat(routing): implement router merging
This commit is contained in:
parent
6eb965adcd
commit
2ce49a3347
3 changed files with 208 additions and 4 deletions
|
|
@ -28,6 +28,7 @@ void lnm_http_router_free(lnm_http_router *router) {
|
|||
|
||||
for (size_t i = 0; i < lnm_http_method_total; i++) {
|
||||
lnm_http_route_free(router->routes[i]);
|
||||
lnm_http_route_free(router->multi_segment_routes[i]);
|
||||
}
|
||||
|
||||
free(router);
|
||||
|
|
@ -287,3 +288,145 @@ lnm_http_route_match_get(lnm_http_route_match *match, const char *key) {
|
|||
|
||||
return &match->key_segments[trie->index];
|
||||
}
|
||||
|
||||
bool lnm_http_router_conflicts(const lnm_http_router *r1,
|
||||
const lnm_http_router *r2) {
|
||||
// First check the literal routes for the routers
|
||||
for (lnm_http_method m = 0; m < lnm_http_method_total; m++) {
|
||||
if ((r1->routes[m] != NULL && r2->routes[m] != NULL) ||
|
||||
(r1->multi_segment_routes[m] != NULL &&
|
||||
r2->multi_segment_routes[m] != NULL)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((r1->single_segment_child != NULL && r2->single_segment_child != NULL) &&
|
||||
lnm_http_router_conflicts(r1->single_segment_child,
|
||||
r2->single_segment_child)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned char c = 0; c < 128; c++) {
|
||||
if ((r1->exact_children[c] != NULL && r2->exact_children[c] != NULL) &&
|
||||
lnm_http_router_conflicts(r1->exact_children[c],
|
||||
r2->exact_children[c])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void __lnm_http_router_merge(lnm_http_router *r1, lnm_http_router *r2) {
|
||||
for (lnm_http_method m = 0; m < lnm_http_method_total; m++) {
|
||||
if (r2->routes[m] != NULL) {
|
||||
r1->routes[m] = r2->routes[m];
|
||||
}
|
||||
|
||||
if (r2->multi_segment_routes[m] != NULL) {
|
||||
r1->multi_segment_routes[m] = r2->multi_segment_routes[m];
|
||||
}
|
||||
}
|
||||
|
||||
if (r1->single_segment_child != NULL && r2->single_segment_child != NULL) {
|
||||
__lnm_http_router_merge(r1->single_segment_child, r2->single_segment_child);
|
||||
} else if (r2->single_segment_child != NULL) {
|
||||
r1->single_segment_child = r2->single_segment_child;
|
||||
}
|
||||
|
||||
for (unsigned char c = 0; c < 128; c++) {
|
||||
if (r1->exact_children[c] != NULL && r2->exact_children[c] != NULL) {
|
||||
__lnm_http_router_merge(r1->exact_children[c], r2->exact_children[c]);
|
||||
} else if (r2->exact_children[c] != NULL) {
|
||||
r1->exact_children[c] = r2->exact_children[c];
|
||||
}
|
||||
}
|
||||
|
||||
r1->represents_route = r1->represents_route || r2->represents_route;
|
||||
|
||||
free(r2);
|
||||
}
|
||||
|
||||
lnm_err lnm_http_router_merge(lnm_http_router *r1, lnm_http_router *r2) {
|
||||
if (lnm_http_router_conflicts(r1, r2)) {
|
||||
return lnm_err_overlapping_route;
|
||||
}
|
||||
|
||||
__lnm_http_router_merge(r1, r2);
|
||||
|
||||
return lnm_err_ok;
|
||||
}
|
||||
|
||||
lnm_err lnm_http_router_nest(lnm_http_router *parent, lnm_http_router *child,
|
||||
const char *prefix) {
|
||||
size_t prefix_len = strlen(prefix);
|
||||
|
||||
if (!is_ascii(prefix) || prefix[0] != '/' || prefix[prefix_len - 1] == '/') {
|
||||
return lnm_err_invalid_route;
|
||||
}
|
||||
|
||||
lnm_http_router *merge_target = parent;
|
||||
|
||||
// The child router's routes are also mounted on '/', so we stop one earlier
|
||||
// as we need to merge it with the router representing the '/'
|
||||
while (*(prefix + 1) != '\0') {
|
||||
unsigned char c = *prefix;
|
||||
|
||||
if (merge_target->exact_children[c] == NULL) {
|
||||
LNM_RES(lnm_http_router_init(&merge_target->exact_children[c]));
|
||||
}
|
||||
|
||||
merge_target = merge_target->exact_children[c];
|
||||
prefix++;
|
||||
}
|
||||
|
||||
// We check whether there are any conflicts between the child and the existing
|
||||
// router
|
||||
bool conflicting = merge_target->single_segment_child != NULL &&
|
||||
child->single_segment_child != NULL;
|
||||
|
||||
for (lnm_http_method m = 0; !conflicting && m < lnm_http_method_total; m++) {
|
||||
conflicting =
|
||||
conflicting ||
|
||||
(merge_target->routes[m] != NULL && child->routes[m] != NULL) ||
|
||||
(merge_target->multi_segment_routes[m] != NULL &&
|
||||
child->multi_segment_routes[m] != NULL);
|
||||
}
|
||||
|
||||
for (unsigned char c = 0; !conflicting && c < 128; c++) {
|
||||
conflicting = conflicting || (merge_target->exact_children[c] != NULL &&
|
||||
child->exact_children[c] != NULL);
|
||||
}
|
||||
|
||||
if (conflicting) {
|
||||
return lnm_err_overlapping_route;
|
||||
}
|
||||
|
||||
// Merge routers
|
||||
merge_target->represents_route =
|
||||
merge_target->represents_route || child->represents_route;
|
||||
|
||||
if (child->single_segment_child != NULL) {
|
||||
merge_target->single_segment_child = child->single_segment_child;
|
||||
}
|
||||
|
||||
for (lnm_http_method m = 0; m < lnm_http_method_total; m++) {
|
||||
if (child->routes[m] != NULL) {
|
||||
merge_target->routes[m] = child->routes[m];
|
||||
}
|
||||
|
||||
if (child->multi_segment_routes[m] != NULL) {
|
||||
merge_target->multi_segment_routes[m] = child->multi_segment_routes[m];
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned char c = 0; c < 128; c++) {
|
||||
if (child->exact_children[c] != NULL) {
|
||||
merge_target->exact_children[c] = child->exact_children[c];
|
||||
}
|
||||
}
|
||||
|
||||
free(child);
|
||||
|
||||
return lnm_err_ok;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue