From 9e8404975e3326de4d29bd6a6a36e6e219bc98fe Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 8 May 2020 00:13:40 +0800 Subject: [PATCH] update Signed-off-by: Bo-Yi Wu --- gin.go | 44 +++++++- tree.go | 320 +++++++++++++++++++++++++++++++------------------------- 2 files changed, 220 insertions(+), 144 deletions(-) diff --git a/gin.go b/gin.go index 7c337d23..49091303 100644 --- a/gin.go +++ b/gin.go @@ -113,6 +113,8 @@ type Engine struct { noMethod HandlersChain pool sync.Pool trees methodTrees + // paramsPool sync.Pool + maxParams uint16 } var _ IRouter = &Engine{} @@ -256,6 +258,8 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { assert1(len(handlers) > 0, "there must be at least one handler") debugPrintRoute(method, path, handlers) + // varsCount := uint16(0) + root := engine.trees.get(method) if root == nil { root = new(node) @@ -263,6 +267,19 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { engine.trees = append(engine.trees, methodTree{method: method, root: root}) } root.addRoute(path, handlers) + + // Update maxParams + // if paramsCount := countParams(path); paramsCount+varsCount > root.maxParams { + // engine.maxParams = paramsCount + varsCount + // } + + // Lazy-init paramsPool alloc func + // if engine.paramsPool.New == nil && engine.maxParams > 0 { + // engine.paramsPool.New = func() interface{} { + // ps := make(Params, 0, engine.maxParams) + // return &ps + // } + // } } // Routes returns a slice of registered routes, including some useful information, such as: @@ -381,6 +398,23 @@ func (engine *Engine) HandleContext(c *Context) { c.index = oldIndexValue } +// func (engine *Engine) getParams() *Params { +// // c := engine.pool.Get().(*Context) +// // c.Params = c.Params[0:0] +// ps := engine.paramsPool.Get().(*Params) +// *ps = (*ps)[0:0] // reset slice +// return ps +// } + +// func (engine *Engine) putParams(ps *Params) { +// // c := engine.pool.Get().(*Context) +// // c.Params = *ps +// // engine.pool.Put(c) +// if ps != nil { +// engine.paramsPool.Put(ps) +// } +// } + func (engine *Engine) handleHTTPRequest(c *Context) { httpMethod := c.Request.Method rPath := c.Request.URL.Path @@ -402,10 +436,16 @@ func (engine *Engine) handleHTTPRequest(c *Context) { } root := t[i].root // Find route in tree - value := root.getValue(rPath, c.Params, unescape) + value := root.getValue(rPath, &c.Params, unescape) + if value.params != nil { + // engine.putParams(value.params) + c.Params = *value.params + } if value.handlers != nil { c.handlers = value.handlers - c.Params = value.params + // if value.params != nil { + // c.Params = *value.params + // } // c.fullPath = value.fullPath c.Next() c.writermem.WriteHeaderNow() diff --git a/tree.go b/tree.go index e3180f47..d2aec06c 100644 --- a/tree.go +++ b/tree.go @@ -5,7 +5,6 @@ package gin import ( - "net/url" "strings" "unicode" "unicode/utf8" @@ -132,23 +131,23 @@ func (n *node) incrementChildPrio(pos int) int { func (n *node) addRoute(path string, handlers HandlersChain) { fullPath := path n.priority++ - numParams := countParams(path) + // numParams := countParams(path) // Empty tree if len(n.path) == 0 && len(n.children) == 0 { - n.insertChild(numParams, path, fullPath, handlers) + n.insertChild(path, fullPath, handlers) n.nType = root return } - parentFullPathIndex := 0 + // parentFullPathIndex := 0 walk: for { // Update maxParams of the current node - if numParams > n.maxParams { - n.maxParams = numParams - } + // if numParams > n.maxParams { + // n.maxParams = numParams + // } // Find the longest common prefix. // This also implies that the common prefix contains no ':' or '*' @@ -168,11 +167,11 @@ walk: } // Update maxParams (max of all children) - for _, v := range child.children { - if v.maxParams > child.maxParams { - child.maxParams = v.maxParams - } - } + // for _, v := range child.children { + // if v.maxParams > child.maxParams { + // child.maxParams = v.maxParams + // } + // } n.children = []*node{&child} // []byte for proper unicode char conversion, see #65 @@ -188,15 +187,15 @@ walk: path = path[i:] if n.wildChild { - parentFullPathIndex += len(n.path) + // parentFullPathIndex += len(n.path) n = n.children[0] n.priority++ // Update maxParams of the child node - if numParams > n.maxParams { - n.maxParams = numParams - } - numParams-- + // if numParams > n.maxParams { + // n.maxParams = numParams + // } + // numParams-- // Check if the wildcard matches if len(path) >= len(n.path) && n.path == path[:len(n.path)] { @@ -222,7 +221,7 @@ walk: // slash after param if n.nType == param && c == '/' && len(n.children) == 1 { - parentFullPathIndex += len(n.path) + // parentFullPathIndex += len(n.path) n = n.children[0] n.priority++ continue walk @@ -231,7 +230,7 @@ walk: // Check if a child with the next path byte exists for i, max := 0, len(n.indices); i < max; i++ { if c == n.indices[i] { - parentFullPathIndex += len(n.path) + // parentFullPathIndex += len(n.path) i = n.incrementChildPrio(i) n = n.children[i] continue walk @@ -243,14 +242,14 @@ walk: // []byte for proper unicode char conversion, see #65 n.indices += string([]byte{c}) child := &node{ - maxParams: numParams, + // maxParams: numParams, // fullPath: fullPath, } n.children = append(n.children, child) n.incrementChildPrio(len(n.indices) - 1) n = child } - n.insertChild(numParams, path, fullPath, handlers) + n.insertChild(path, fullPath, handlers) return } @@ -288,8 +287,8 @@ func findWildcard(path string) (wildcard string, i int, valid bool) { return "", -1, false } -func (n *node) insertChild(numParams uint16, path string, fullPath string, handlers HandlersChain) { - for numParams > 0 { +func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) { + for { // Find prefix until first wildcard wildcard, i, valid := findWildcard(path) if i < 0 { // No wildcard found @@ -323,15 +322,15 @@ func (n *node) insertChild(numParams uint16, path string, fullPath string, handl n.wildChild = true child := &node{ - nType: param, - path: wildcard, - maxParams: numParams, + nType: param, + path: wildcard, + // maxParams: numParams, // fullPath: fullPath, } n.children = []*node{child} n = child n.priority++ - numParams-- + // numParams-- // if the path doesn't end with the wildcard, then there // will be another non-wildcard subpath starting with '/' @@ -339,8 +338,8 @@ func (n *node) insertChild(numParams uint16, path string, fullPath string, handl path = path[len(wildcard):] child := &node{ - maxParams: numParams, - priority: 1, + // maxParams: numParams, + priority: 1, // fullPath: fullPath, } n.children = []*node{child} @@ -354,7 +353,7 @@ func (n *node) insertChild(numParams uint16, path string, fullPath string, handl } // catchAll - if i+len(wildcard) != len(path) || numParams > 1 { + if i+len(wildcard) != len(path) { panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") } @@ -374,13 +373,13 @@ func (n *node) insertChild(numParams uint16, path string, fullPath string, handl child := &node{ wildChild: true, nType: catchAll, - maxParams: 1, + // maxParams: 1, // fullPath: fullPath, } // update maxParams of the parent node - if n.maxParams < 1 { - n.maxParams = 1 - } + // if n.maxParams < 1 { + // n.maxParams = 1 + // } n.children = []*node{child} n.indices = string('/') n = child @@ -388,11 +387,11 @@ func (n *node) insertChild(numParams uint16, path string, fullPath string, handl // second node: node holding the variable child = &node{ - path: path[i:], - nType: catchAll, - handlers: handlers, - priority: 1, - maxParams: 1, + path: path[i:], + nType: catchAll, + handlers: handlers, + priority: 1, + // maxParams: 1, // fullPath: fullPath, } n.children = []*node{child} @@ -409,7 +408,7 @@ func (n *node) insertChild(numParams uint16, path string, fullPath string, handl // nodeValue holds return values of (*Node).getValue method type nodeValue struct { handlers HandlersChain - params Params + params *Params tsr bool // fullPath string } @@ -419,11 +418,148 @@ type nodeValue struct { // If no handle can be found, a TSR (trailing slash redirect) recommendation is // made if a handle exists with an extra (without the) trailing slash for the // given path. -func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) { - value.params = po +func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { + value = nodeValue{} walk: // Outer loop for walking the tree for { prefix := n.path + + if len(path) > len(prefix) && path[:len(prefix)] == prefix { + path = path[len(prefix):] + // If this node does not have a wildcard (param or catchAll) + // child, we can just look up the next child node and continue + // to walk down the tree + if !n.wildChild { + idxc := path[0] + for i, c := range []byte(n.indices) { + if c == idxc { + n = n.children[i] + continue walk + } + } + + // Nothing found. + // We can recommend to redirect to the same URL without a + // trailing slash if a leaf exists for that path. + value.tsr = (path == "/" && n.handlers != nil) + return + } + + // handle wildcard child + n = n.children[0] + switch n.nType { + case param: + // find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // save param value + // if cap(value.params) < int(n.maxParams) { + // value.params = make(Params, 0, n.maxParams) + // } + // i := len(value.params) + // value.params = value.params[:i+1] // expand slice within preallocated capacity + // value.params[i].Key = n.path[1:] + // val := path[:end] + // if unescape { + // var err error + // if value.params[i].Value, err = url.QueryUnescape(val); err != nil { + // value.params[i].Value = val // fallback, in case of error + // } + // } else { + // value.params[i].Value = val + // } + if params != nil { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + // val := path[:end] + // if unescape { + // if v, err := url.QueryUnescape(val); err == nil { + // val = v + // } + // } + (*value.params)[i] = Param{ + Key: n.path[1:], + Value: path, + } + } + + // we need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + path = path[end:] + n = n.children[0] + continue walk + } + + // ... but we can't + value.tsr = (len(path) == end+1) + return + } + + if value.handlers = n.handlers; value.handlers != nil { + // value.fullPath = n.fullPath + return + } + if len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists for TSR recommendation + n = n.children[0] + value.tsr = (n.path == "/" && n.handlers != nil) + } + return + + case catchAll: + // // save param value + // if cap(value.params) < int(n.maxParams) { + // value.params = make(Params, 0, n.maxParams) + // } + // i := len(value.params) + // value.params = value.params[:i+1] // expand slice within preallocated capacity + // value.params[i].Key = n.path[2:] + // if unescape { + // var err error + // if value.params[i].Value, err = url.QueryUnescape(path); err != nil { + // value.params[i].Value = path // fallback, in case of error + // } + // } else { + // value.params[i].Value = path + // } + + // Save param value + if params != nil { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + // if unescape { + // if v, err := url.QueryUnescape(path); err == nil { + // path = v + // } + // } + (*value.params)[i] = Param{ + Key: n.path[2:], + Value: path, + } + } + + value.handlers = n.handlers + // value.fullPath = n.fullPath + return + + default: + panic("invalid node type") + } + } + if path == prefix { // We should have reached the node containing the handle. // Check if this node has a handle registered. @@ -454,106 +590,6 @@ walk: // Outer loop for walking the tree return } - if len(path) > len(prefix) && path[:len(prefix)] == prefix { - path = path[len(prefix):] - // If this node does not have a wildcard (param or catchAll) - // child, we can just look up the next child node and continue - // to walk down the tree - if !n.wildChild { - c := path[0] - indices := n.indices - for i, max := 0, len(indices); i < max; i++ { - if c == indices[i] { - n = n.children[i] - continue walk - } - } - - // Nothing found. - // We can recommend to redirect to the same URL without a - // trailing slash if a leaf exists for that path. - value.tsr = path == "/" && n.handlers != nil - return - } - - // handle wildcard child - n = n.children[0] - switch n.nType { - case param: - // find param end (either '/' or path end) - end := 0 - for end < len(path) && path[end] != '/' { - end++ - } - - // save param value - if cap(value.params) < int(n.maxParams) { - value.params = make(Params, 0, n.maxParams) - } - i := len(value.params) - value.params = value.params[:i+1] // expand slice within preallocated capacity - value.params[i].Key = n.path[1:] - val := path[:end] - if unescape { - var err error - if value.params[i].Value, err = url.QueryUnescape(val); err != nil { - value.params[i].Value = val // fallback, in case of error - } - } else { - value.params[i].Value = val - } - - // we need to go deeper! - if end < len(path) { - if len(n.children) > 0 { - path = path[end:] - n = n.children[0] - continue walk - } - - // ... but we can't - value.tsr = len(path) == end+1 - return - } - - if value.handlers = n.handlers; value.handlers != nil { - // value.fullPath = n.fullPath - return - } - if len(n.children) == 1 { - // No handle found. Check if a handle for this path + a - // trailing slash exists for TSR recommendation - n = n.children[0] - value.tsr = n.path == "/" && n.handlers != nil - } - return - - case catchAll: - // save param value - if cap(value.params) < int(n.maxParams) { - value.params = make(Params, 0, n.maxParams) - } - i := len(value.params) - value.params = value.params[:i+1] // expand slice within preallocated capacity - value.params[i].Key = n.path[2:] - if unescape { - var err error - if value.params[i].Value, err = url.QueryUnescape(path); err != nil { - value.params[i].Value = path // fallback, in case of error - } - } else { - value.params[i].Value = path - } - - value.handlers = n.handlers - // value.fullPath = n.fullPath - return - - default: - panic("invalid node type") - } - } - // Nothing found. We can recommend to redirect to the same URL with an // extra trailing slash if a leaf exists for that path value.tsr = (path == "/") ||