Optimizes tree lookup

This commit is contained in:
Manu Mtz-Almeida 2015-05-29 21:03:28 +02:00
parent 9584e4ea5c
commit 66e9feb622
2 changed files with 43 additions and 20 deletions

29
gin.go
View File

@ -28,12 +28,12 @@ type (
Engine struct { Engine struct {
RouterGroup RouterGroup
HTMLRender render.HTMLRender HTMLRender render.HTMLRender
pool sync.Pool
allNoRoute HandlersChain allNoRoute HandlersChain
allNoMethod HandlersChain allNoMethod HandlersChain
noRoute HandlersChain noRoute HandlersChain
noMethod HandlersChain noMethod HandlersChain
trees map[string]*node pool sync.Pool
trees methodTrees
// Enables automatic redirection if the current route can't be matched but a // Enables automatic redirection if the current route can't be matched but a
// handler for the path with (without) the trailing slash exists. // handler for the path with (without) the trailing slash exists.
@ -75,7 +75,7 @@ func New() *Engine {
RedirectTrailingSlash: true, RedirectTrailingSlash: true,
RedirectFixedPath: true, RedirectFixedPath: true,
HandleMethodNotAllowed: true, HandleMethodNotAllowed: true,
trees: make(map[string]*node), trees: make(methodTrees, 0, 6),
} }
engine.RouterGroup.engine = engine engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} { engine.pool.New = func() interface{} {
@ -155,10 +155,13 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
panic("there must be at least one handler") panic("there must be at least one handler")
} }
root := engine.trees[method] root := engine.trees.get("method")
if root == nil { if root == nil {
root = new(node) root = new(node)
engine.trees[method] = root engine.trees = append(engine.trees, methodTree{
method: method,
root: root,
})
} }
root.addRoute(path, handlers) root.addRoute(path, handlers)
} }
@ -210,9 +213,11 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
path := context.Request.URL.Path path := context.Request.URL.Path
// Find root of the tree for the given HTTP method // Find root of the tree for the given HTTP method
if root := engine.trees[httpMethod]; root != nil { t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method == httpMethod {
// Find route in tree // Find route in tree
handlers, params, tsr := root.getValue(path, context.Params) handlers, params, tsr := t[i].root.getValue(path, context.Params)
if handlers != nil { if handlers != nil {
context.handlers = handlers context.handlers = handlers
context.Params = params context.Params = params
@ -221,16 +226,18 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
return return
} else if httpMethod != "CONNECT" && path != "/" { } else if httpMethod != "CONNECT" && path != "/" {
if engine.serveAutoRedirect(context, root, tsr) { if engine.serveAutoRedirect(context, t[i].root, tsr) {
return return
} }
} }
} }
}
// TODO: unit test
if engine.HandleMethodNotAllowed { if engine.HandleMethodNotAllowed {
for method, root := range engine.trees { for _, tree := range engine.trees {
if method != httpMethod { if tree.method != httpMethod {
if handlers, _, _ := root.getValue(path, nil); handlers != nil { if handlers, _, _ := tree.root.getValue(path, nil); handlers != nil {
context.handlers = engine.allNoMethod context.handlers = engine.allNoMethod
serveError(context, 405, default405Body) serveError(context, 405, default405Body)
return return

16
tree.go
View File

@ -36,6 +36,22 @@ func (ps Params) ByName(name string) (va string) {
return return
} }
type methodTree struct {
method string
root *node
}
type methodTrees []methodTree
func (trees methodTrees) get(method string) *node {
for _, tree := range trees {
if tree.method == method {
return tree.root
}
}
return nil
}
func min(a, b int) int { func min(a, b int) int {
if a <= b { if a <= b {
return a return a