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

47
gin.go
View File

@ -28,12 +28,12 @@ type (
Engine struct {
RouterGroup
HTMLRender render.HTMLRender
pool sync.Pool
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute 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
// handler for the path with (without) the trailing slash exists.
@ -75,7 +75,7 @@ func New() *Engine {
RedirectTrailingSlash: true,
RedirectFixedPath: true,
HandleMethodNotAllowed: true,
trees: make(map[string]*node),
trees: make(methodTrees, 0, 6),
}
engine.RouterGroup.engine = engine
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")
}
root := engine.trees[method]
root := engine.trees.get("method")
if root == nil {
root = new(node)
engine.trees[method] = root
engine.trees = append(engine.trees, methodTree{
method: method,
root: root,
})
}
root.addRoute(path, handlers)
}
@ -210,27 +213,31 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
path := context.Request.URL.Path
// Find root of the tree for the given HTTP method
if root := engine.trees[httpMethod]; root != nil {
// Find route in tree
handlers, params, tsr := root.getValue(path, context.Params)
if handlers != nil {
context.handlers = handlers
context.Params = params
context.Next()
context.writermem.WriteHeaderNow()
return
} else if httpMethod != "CONNECT" && path != "/" {
if engine.serveAutoRedirect(context, root, tsr) {
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method == httpMethod {
// Find route in tree
handlers, params, tsr := t[i].root.getValue(path, context.Params)
if handlers != nil {
context.handlers = handlers
context.Params = params
context.Next()
context.writermem.WriteHeaderNow()
return
} else if httpMethod != "CONNECT" && path != "/" {
if engine.serveAutoRedirect(context, t[i].root, tsr) {
return
}
}
}
}
// TODO: unit test
if engine.HandleMethodNotAllowed {
for method, root := range engine.trees {
if method != httpMethod {
if handlers, _, _ := root.getValue(path, nil); handlers != nil {
for _, tree := range engine.trees {
if tree.method != httpMethod {
if handlers, _, _ := tree.root.getValue(path, nil); handlers != nil {
context.handlers = engine.allNoMethod
serveError(context, 405, default405Body)
return

16
tree.go
View File

@ -36,6 +36,22 @@ func (ps Params) ByName(name string) (va string) {
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 {
if a <= b {
return a