tree: sync httprouter update (#2173)

This commit is contained in:
thinkerou 2019-12-09 15:04:35 +08:00 committed by GitHub
parent 6e16da8683
commit 168fa94516
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 89 additions and 75 deletions

164
tree.go
View File

@ -264,71 +264,80 @@ walk:
} }
} }
func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) { // Search for a wildcard segment and check the name for invalid characters.
var offset int // already handled bytes of the path // Returns -1 as index, if no wildcard war found.
func findWildcard(path string) (wildcard string, i int, valid bool) {
// Find prefix until first wildcard (beginning with ':' or '*') // Find start
for i, max := 0, len(path); numParams > 0; i++ { for start, c := range []byte(path) {
c := path[i] // A wildcard starts with ':' (param) or '*' (catch-all)
if c != ':' && c != '*' { if c != ':' && c != '*' {
continue continue
} }
// Find wildcard end (either '/' or path end) and check the name for invalid characters // Find end and check for invalid characters
end := i + 1 valid = true
invalid := false for end, c := range []byte(path[start+1:]) {
for end < max { switch c {
c := path[end] case '/':
if c == '/' { return path[start : start+1+end], start, valid
break case ':', '*':
valid = false
} }
if c == ':' || c == '*' { }
invalid = true return path[start:], start, valid
} }
end++ return "", -1, false
}
func (n *node) insertChild(numParams uint8, path string, fullPath string, handlers HandlersChain) {
for numParams > 0 {
// Find prefix until first wildcard
wildcard, i, valid := findWildcard(path)
if i < 0 { // No wildcard found
break
} }
// The wildcard name must not contain ':' and '*' // The wildcard name must not contain ':' and '*'
if invalid { if !valid {
panic("only one wildcard per path segment is allowed, has: '" + panic("only one wildcard per path segment is allowed, has: '" +
path[i:end] + "' in path '" + fullPath + "'") wildcard + "' in path '" + fullPath + "'")
} }
// check if the wildcard has a name // check if the wildcard has a name
if end-i < 2 { if len(wildcard) < 2 {
panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
} }
// Check if this node has existing children which would be // Check if this node has existing children which would be
// unreachable if we insert the wildcard here // unreachable if we insert the wildcard here
if len(n.children) > 0 { if len(n.children) > 0 {
panic("wildcard route '" + path[i:end] + panic("wildcard segment '" + wildcard +
"' conflicts with existing children in path '" + fullPath + "'") "' conflicts with existing children in path '" + fullPath + "'")
} }
if c == ':' { // param if wildcard[0] == ':' { // param
// split path at the beginning of the wildcard
if i > 0 { if i > 0 {
n.path = path[offset:i] // Insert prefix before the current wildcard
offset = i n.path = path[:i]
path = path[i:]
} }
n.wildChild = true
child := &node{ child := &node{
nType: param, nType: param,
path: wildcard,
maxParams: numParams, maxParams: numParams,
fullPath: fullPath, fullPath: fullPath,
} }
n.children = []*node{child} n.children = []*node{child}
n.wildChild = true
n = child n = child
n.priority++ n.priority++
numParams-- numParams--
// if the path doesn't end with the wildcard, then there // if the path doesn't end with the wildcard, then there
// will be another non-wildcard subpath starting with '/' // will be another non-wildcard subpath starting with '/'
if end < max { if len(wildcard) < len(path) {
n.path = path[offset:end] path = path[len(wildcard):]
offset = end
child := &node{ child := &node{
maxParams: numParams, maxParams: numParams,
@ -337,58 +346,63 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle
} }
n.children = []*node{child} n.children = []*node{child}
n = child n = child
continue
} }
} else { // catchAll // Otherwise we're done. Insert the handle in the new leaf
if end != max || numParams > 1 { n.handlers = handlers
panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
}
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
}
// currently fixed width 1 for '/'
i--
if path[i] != '/' {
panic("no / before catch-all in path '" + fullPath + "'")
}
n.path = path[offset:i]
// first node: catchAll node with empty path
child := &node{
wildChild: true,
nType: catchAll,
maxParams: 1,
fullPath: fullPath,
}
// update maxParams of the parent node
if n.maxParams < 1 {
n.maxParams = 1
}
n.children = []*node{child}
n.indices = string(path[i])
n = child
n.priority++
// second node: node holding the variable
child = &node{
path: path[i:],
nType: catchAll,
maxParams: 1,
handlers: handlers,
priority: 1,
fullPath: fullPath,
}
n.children = []*node{child}
return return
} }
// catchAll
if i+len(wildcard) != len(path) || numParams > 1 {
panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
}
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
}
// currently fixed width 1 for '/'
i--
if path[i] != '/' {
panic("no / before catch-all in path '" + fullPath + "'")
}
n.path = path[:i]
// First node: catchAll node with empty path
child := &node{
wildChild: true,
nType: catchAll,
maxParams: 1,
fullPath: fullPath,
}
// update maxParams of the parent node
if n.maxParams < 1 {
n.maxParams = 1
}
n.children = []*node{child}
n.indices = string('/')
n = child
n.priority++
// second node: node holding the variable
child = &node{
path: path[i:],
nType: catchAll,
maxParams: 1,
handlers: handlers,
priority: 1,
fullPath: fullPath,
}
n.children = []*node{child}
return
} }
// insert remaining path part and handle to the leaf // If no wildcard was found, simple insert the path and handle
n.path = path[offset:] n.path = path
n.handlers = handlers n.handlers = handlers
n.fullPath = fullPath n.fullPath = fullPath
} }