Use backtracking

Signed-off-by: Yue Yang <g1enyy0ung@gmail.com>
This commit is contained in:
Yue Yang 2021-04-29 09:21:29 +08:00
parent 7c9e3b0e29
commit 3bb5308004
2 changed files with 37 additions and 17 deletions

41
tree.go
View File

@ -118,6 +118,11 @@ type node struct {
fullPath string fullPath string
} }
type skip struct {
path string
paramNode *node
}
// Increments priority of the given child and reorders if necessary // Increments priority of the given child and reorders if necessary
func (n *node) incrementChildPrio(pos int) int { func (n *node) incrementChildPrio(pos int) int {
cs := n.children cs := n.children
@ -400,6 +405,8 @@ type nodeValue struct {
// made if a handle exists with an extra (without the) trailing slash for the // made if a handle exists with an extra (without the) trailing slash for the
// given path. // given path.
func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) { func (n *node) getValue(path string, params *Params, unescape bool) (value nodeValue) {
var skipped *skip
walk: // Outer loop for walking the tree walk: // Outer loop for walking the tree
for { for {
prefix := n.path prefix := n.path
@ -411,23 +418,22 @@ walk: // Outer loop for walking the tree
idxc := path[0] idxc := path[0]
for i, c := range []byte(n.indices) { for i, c := range []byte(n.indices) {
if c == idxc { if c == idxc {
childWillWalkTo := n.children[i] if strings.HasPrefix(n.children[len(n.children)-1].path, ":") {
skipped = &skip{
if strings.Split(childWillWalkTo.path, "/")[0] != strings.Split(path, "/")[0] && strings.HasPrefix(n.children[len(n.children)-1].path, ":") { path: prefix + path,
for _, child := range childWillWalkTo.children { paramNode: &node{
cPath := string(c) + child.path path: n.path,
wildChild: n.wildChild,
if cPath == path { nType: n.nType,
child.path = cPath priority: n.priority,
n = child children: n.children,
continue walk handlers: n.handlers,
} fullPath: n.fullPath,
},
} }
continue
} }
n = childWillWalkTo n = n.children[i]
continue walk continue walk
} }
} }
@ -558,6 +564,13 @@ walk: // Outer loop for walking the tree
return return
} }
if path != "/" && strings.HasSuffix(skipped.path, path) {
path = skipped.path
n = skipped.paramNode
skipped = nil
continue walk
}
// Nothing found. We can recommend to redirect to the same URL with an // Nothing found. We can recommend to redirect to the same URL with an
// extra trailing slash if a leaf exists for that path // extra trailing slash if a leaf exists for that path
value.tsr = (path == "/") || value.tsr = (path == "/") ||

View File

@ -135,9 +135,10 @@ func TestTreeWildcard(t *testing.T) {
routes := [...]string{ routes := [...]string{
"/", "/",
"/cmd/:tool/:sub",
"/cmd/:tool/", "/cmd/:tool/",
"/cmd/:tool/:sub",
"/cmd/whoami", "/cmd/whoami",
"/cmd/whoami/root",
"/cmd/whoami/root/", "/cmd/whoami/root/",
"/src/*filepath", "/src/*filepath",
"/search/", "/search/",
@ -152,6 +153,7 @@ func TestTreeWildcard(t *testing.T) {
"/doc/go1.html", "/doc/go1.html",
"/info/:user/public", "/info/:user/public",
"/info/:user/project/:project", "/info/:user/project/:project",
"/info/:user/project/golang",
} }
for _, route := range routes { for _, route := range routes {
tree.addRoute(route, fakeHandler(route)) tree.addRoute(route, fakeHandler(route))
@ -161,11 +163,15 @@ func TestTreeWildcard(t *testing.T) {
{"/", false, "/", nil}, {"/", false, "/", nil},
{"/cmd/test", true, "/cmd/:tool/", Params{Param{"tool", "test"}}}, {"/cmd/test", true, "/cmd/:tool/", Params{Param{"tool", "test"}}},
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}}, {"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "test"}, Param{Key: "sub", Value: "3"}}},
{"/cmd/who", true, "/cmd/:tool/", Params{Param{"tool", "who"}}},
{"/cmd/who/", false, "/cmd/:tool/", Params{Param{"tool", "who"}}},
{"/cmd/whoami", false, "/cmd/whoami", nil}, {"/cmd/whoami", false, "/cmd/whoami", nil},
{"/cmd/whoami/", true, "/cmd/whoami", nil}, {"/cmd/whoami/", true, "/cmd/whoami", nil},
{"/cmd/whoami/r", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "whoami"}, Param{Key: "sub", Value: "r"}}},
{"/cmd/whoami/r/", true, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "whoami"}, Param{Key: "sub", Value: "r"}}},
{"/cmd/whoami/root", false, "/cmd/whoami/root", nil},
{"/cmd/whoami/root/", false, "/cmd/whoami/root/", nil}, {"/cmd/whoami/root/", false, "/cmd/whoami/root/", nil},
{"/cmd/whoami/root", true, "/cmd/whoami/root/", nil},
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{Key: "tool", Value: "test"}, Param{Key: "sub", Value: "3"}}},
{"/src/", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/"}}}, {"/src/", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/"}}},
{"/src/some/file.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file.png"}}}, {"/src/some/file.png", false, "/src/*filepath", Params{Param{Key: "filepath", Value: "/some/file.png"}}},
{"/search/", false, "/search/", nil}, {"/search/", false, "/search/", nil},
@ -179,6 +185,7 @@ func TestTreeWildcard(t *testing.T) {
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{Key: "dir", Value: "js"}, Param{Key: "filepath", Value: "/inc/framework.js"}}}, {"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{Key: "dir", Value: "js"}, Param{Key: "filepath", Value: "/inc/framework.js"}}},
{"/info/gordon/public", false, "/info/:user/public", Params{Param{Key: "user", Value: "gordon"}}}, {"/info/gordon/public", false, "/info/:user/public", Params{Param{Key: "user", Value: "gordon"}}},
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "gordon"}, Param{Key: "project", Value: "go"}}}, {"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{Key: "user", Value: "gordon"}, Param{Key: "project", Value: "go"}}},
{"/info/gordon/project/golang", false, "/info/:user/project/golang", Params{Param{Key: "user", Value: "gordon"}}},
}) })
checkPriorities(t, tree) checkPriorities(t, tree)