Router Design
csilk uses a Radix Tree (compact prefix tree) for efficient route matching. The router supports static routes, named parameters (:param), and wildcards (*wildcard).
Node Structure
graph TB
subgraph "csilk_router_node_t"
SEG["segment: 'users'"]
TYPE["type: STATIC | PARAM | WILDCARD"]
MH["method_handlers: linked list\n GET → [handler1, handler2, NULL]\n POST → [handler1, NULL]"]
CH["children[0..127]: fixed array\n children_count: 3"]
end
Radix Tree Visualization
graph TB
ROOT["/ (root, STATIC)"]
ROOT --> API["api (STATIC)"]
API --> V1["v1 (STATIC)"]
API --> V2["v2 (STATIC)"]
V1 --> US["users (STATIC)"]
V2 --> US2["users (STATIC)"]
US --> GET1["(handlers: GET)"]
US2 --> GET2["(handlers: GET)"]
ROOT --> USERS["users (STATIC)"]
USERS --> PID[":id (PARAM)"]
PID --> PROFILE["profile (STATIC)"]
PID --> POSTS["posts (STATIC)"]
PROFILE --> GET3["(handlers: GET)"]
POSTS --> GET4["(handlers: GET)"]
ROOT --> STATIC["static (STATIC)"]
STATIC --> WF["*filepath (WILDCARD)"]
WF --> GET5["(handlers: GET)"]
Route Registration Flow
sequenceDiagram
participant App
participant Router as csilk_router_t
participant Root as Root Node
participant Node as Child Nodes
App->>Router: csilk_router_add(r, "GET", "/api/v1/users", handlers, 1)
Note over Router: Split path: ["api", "v1", "users"]
loop For each segment
Router->>Root: find_or_create("api", STATIC)
Root->>Root: Search children for STATIC "api"
alt Not found
Root->>Node: node_new("api", STATIC)
Root->>Root: children[idx] = new_node
end
Router->>Node: find_or_create("v1", STATIC)
Node->>Node: Search children for STATIC "v1"
alt Not found
Node->>Node: node_new("v1", STATIC)
end
Router->>Node: find_or_create("users", STATIC)
end
Note over Node: At terminal node "users"
Router->>Node: check_no_duplicate_method("GET")
Node->>Node: malloc(method_handler)
Node->>Node: mh->method = "GET"
Node->>Node: mh->handlers = [handler, NULL]
Node->>Node: prepend to handlers linked list
Route Matching Flow
sequenceDiagram
participant C as csilk_ctx_t
participant R as match_node()
participant N as Router Node
participant P as "Path: /users/42/profile"
C->>R: match_node(root, "GET", "/users/42/profile", ctx)
R->>R: seg = get_next_segment() → "users"
loop For each child of root
R->>N: Child "users" (STATIC)
N->>N: strcmp("users", "users") == 0 ✓
R->>R: match_node(child, "GET", "/42/profile", ctx)
Note over R: Recursive call with remaining path
R->>R: seg = get_next_segment() → "42"
loop For each child of "users"
R->>N: Child ":id" (PARAM)
N->>N: Type is PARAM → capture "42"
N->>P: ctx->params[0] = {":id", "42"}
R->>R: match_node(child, "GET", "/profile", ctx)
R->>R: seg = get_next_segment() → "profile"
loop For each child of ":id"
R->>N: Child "profile" (STATIC)
N->>N: strcmp("profile", "profile") == 0 ✓
R->>R: match_node(child, "GET", "/", ctx)
Note over R: Path exhausted (/ or empty)
N->>N: Lookup method handler for "GET"
N-->>R: return [handler, NULL]
end
end
end
R-->>C: ctx->handlers = matched_handler_array
C->>C: ctx->handler_index = -1
Parameter Matching
flowchart TB
subgraph "Parameter Types"
STATIC["STATIC: Exact string match\n/users/profile"]
PARAM["PARAM: Named capture\n/users/:id\n → ctx->params[0] = {':id', '42'}"]
WILD["WILDCARD: Greedy suffix\n/static/*filepath\n → ctx->params[0] = {'*filepath', 'css/app.css'}"]
end
- STATIC nodes match exact path segments (case-sensitive).
- PARAM nodes match any single path segment and capture the value. Backtracking is supported if the match fails deeper in the tree.
- WILDCARD nodes greedily match the entire remaining path. The router stops traversal at the wildcard node and checks for a handler.
Performance Characteristics
| Operation | Complexity | Notes |
|---|---|---|
| Route Insertion | O(L) | L = path segment count |
| Route Matching (best) | O(S) | S = segment count, exact static match |
| Route Matching (worst) | O(N + P) | N = static children, P = param branches |
| Memory per Route | O(S) | One node per path segment |
The fixed-size children array (CSILK_MAX_CHILDREN = 128) trades some memory for CPU cache locality, ensuring fast linear scans over child nodes.