Skip to content

Commit 5df6147

Browse files
authored
perf: use a non-recursive DFS algorithm for routing (#52)
1 parent a8f9998 commit 5df6147

File tree

3 files changed

+36
-38
lines changed

3 files changed

+36
-38
lines changed

kid.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ type (
4444
)
4545

4646
// Version of Kid.
47-
const Version string = "v0.1.0"
47+
const Version string = "v0.2.0"
4848

4949
// allMethods is a list of all HTTP methods.
5050
var allMethods = []string{

tree.go

+34-32
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type (
3939

4040
// Node is a tree node.
4141
Node struct {
42-
// id is the id of each node. It separates every node from each other.
42+
// id of each node. It separates nodes from each other.
4343
id uint32
4444

4545
// label of the node.
@@ -213,54 +213,56 @@ func isStar(label string) bool {
213213
}
214214

215215
// searchDFS searches the tree with the DFS search algorithm.
216-
func searchDFS(
217-
stack []*Node,
218-
visitedMap map[uint32]bool,
219-
params Params,
220-
path []string,
221-
pos int,
222-
) (map[string]handlerMiddleware, Params, bool) {
223-
if len(stack) == 0 {
224-
return nil, params, false
225-
}
216+
func (t Tree) searchDFS(path []string) (map[string]handlerMiddleware, Params, bool) {
217+
stack := []*Node{t.root}
218+
visitedMap := map[uint32]bool{}
219+
params := make(Params)
220+
var pos int
226221

227-
node := stack[len(stack)-1] // accessing last element
222+
// Search while stack is not empty.
223+
SearchLoop:
224+
for len(stack) != 0 {
225+
// Accessing last element.
226+
node := stack[len(stack)-1]
228227

229-
if !visitedMap[node.id] {
230-
visitedMap[node.id] = true
228+
if !visitedMap[node.id] {
229+
visitedMap[node.id] = true
231230

232-
if node.isParam {
233-
params[node.label] = node.getPathParam(path, pos)
231+
if node.isParam {
232+
params[node.label] = node.getPathParam(path, pos)
233+
}
234+
235+
if node.searchFinished(path, pos) {
236+
return node.handlerMap, params, true
237+
}
234238
}
235239

236-
if node.searchFinished(path, pos) {
237-
return node.handlerMap, params, true
240+
for _, child := range node.children {
241+
if !visitedMap[child.id] && child.doesMatch(path, pos+1) {
242+
// Push child to the stack.
243+
stack = append(stack, child)
244+
pos++
245+
continue SearchLoop
246+
}
238247
}
239-
}
240248

241-
for _, child := range node.children {
242-
if !visitedMap[child.id] && child.doesMatch(path, pos+1) {
243-
stack = append(stack, child)
244-
return searchDFS(stack, visitedMap, params, path, pos+1)
249+
if node.isParam {
250+
delete(params, node.label)
245251
}
246-
}
247252

248-
if node.isParam {
249-
delete(params, node.label)
253+
// Pop from stack.
254+
stack = stack[:len(stack)-1]
255+
pos--
250256
}
251257

252-
stack = stack[:len(stack)-1]
253-
return searchDFS(stack, visitedMap, params, path, pos-1)
258+
return nil, params, false
254259
}
255260

256261
// search searches the Tree and tries to match the path to a handler if possible.
257262
func (t Tree) search(path, method string) (handlerMiddleware, Params, error) {
258263
segments := strings.Split(path, "/")
259-
stack := []*Node{t.root}
260-
visitedMap := map[uint32]bool{}
261-
params := make(Params)
262264

263-
hmMap, params, found := searchDFS(stack, visitedMap, params, segments, 0)
265+
hmMap, params, found := t.searchDFS(segments)
264266

265267
if !found {
266268
return handlerMiddleware{}, params, errNotFound

tree_test.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,7 @@ func TestDFS(t *testing.T) {
215215

216216
for i, testCase := range testCases {
217217
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
218-
stack := []*Node{tree.root}
219-
vm := map[uint32]bool{}
220-
params := make(Params)
221-
222-
hmMap, params, found := searchDFS(stack, vm, params, testCase.path, 0)
218+
hmMap, params, found := tree.searchDFS(testCase.path)
223219
hm, ok := hmMap[testCase.method]
224220

225221
assert.Equal(t, testCase.found, found)

0 commit comments

Comments
 (0)