Permalink
| package lion | |
| import ( | |
| "sort" | |
| "strings" | |
| ) | |
| type nodeType uint8 | |
| const ( | |
| static nodeType = iota // /hello | |
| regexp // TODO: /:id(regex) | |
| param // /:id | |
| wildcard // * | |
| ) | |
| type tree struct { | |
| subtrees map[string]*node | |
| } | |
| func newTree() *tree { | |
| return &tree{ | |
| subtrees: make(map[string]*node), | |
| } | |
| } | |
| func (t *tree) addRoute(method, pattern string, handler Handler) { | |
| root := t.subtrees[method] | |
| if root == nil { | |
| root = &node{} | |
| t.subtrees[method] = root | |
| } | |
| root.addRoute(pattern, handler) | |
| } | |
| type node struct { | |
| nodeType nodeType | |
| pattern string | |
| handler Handler | |
| children typesToNodes | |
| label byte | |
| endinglabel byte | |
| } | |
| func (n *node) isLeaf() bool { | |
| return n.handler != nil | |
| } | |
| func (n *node) addRoute(pattern string, handler Handler) { | |
| search := pattern | |
| if len(search) == 0 { | |
| n.handler = handler | |
| return | |
| } | |
| child := n.getEdge(search[0]) | |
| if child == nil { | |
| child = &node{ | |
| label: search[0], | |
| pattern: search, | |
| handler: handler, | |
| } | |
| n.addChild(child) | |
| return | |
| } | |
| if child.nodeType > static { | |
| pos := stringsIndex(search, '/') | |
| if pos < 0 { | |
| pos = len(search) | |
| } | |
| search = search[pos:] | |
| child.addRoute(search, handler) | |
| return | |
| } | |
| commonPrefix := child.longestPrefix(search) | |
| if commonPrefix == len(child.pattern) { | |
| search = search[commonPrefix:] | |
| child.addRoute(search, handler) | |
| return | |
| } | |
| subchild := &node{ | |
| nodeType: static, | |
| pattern: search[:commonPrefix], | |
| } | |
| n.replaceChild(search[0], subchild) | |
| c2 := child | |
| c2.label = child.pattern[commonPrefix] | |
| subchild.addChild(c2) | |
| child.pattern = child.pattern[commonPrefix:] | |
| search = search[commonPrefix:] | |
| if len(search) == 0 { | |
| subchild.handler = handler | |
| return | |
| } | |
| subchild.addChild(&node{ | |
| label: search[0], | |
| nodeType: static, | |
| pattern: search, | |
| handler: handler, | |
| }) | |
| return | |
| } | |
| func (n *node) getEdge(label byte) *node { | |
| for _, nds := range n.children { | |
| for _, n := range nds { | |
| if n.label == label { | |
| return n | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| func (n *node) replaceChild(label byte, child *node) { | |
| for i := 0; i < len(n.children[child.nodeType]); i++ { | |
| if n.children[child.nodeType][i].label == label { | |
| n.children[child.nodeType][i] = child | |
| n.children[child.nodeType][i].label = label | |
| return | |
| } | |
| } | |
| panic("cannot replace child") | |
| } | |
| func (n *node) findNode(c *Context, path string) (*node, *Context) { | |
| root := n | |
| search := path | |
| LOOP: | |
| for { | |
| if len(search) == 0 { | |
| break | |
| } | |
| l := len(root.children) | |
| for i := 0; i < l; i++ { | |
| t := nodeType(i) | |
| if len(root.children[i]) == 0 { | |
| continue | |
| } | |
| var label byte | |
| if len(search) > 0 { | |
| label = search[0] | |
| } | |
| xn := root.findEdge(nodeType(t), label) | |
| if xn == nil { | |
| continue | |
| } | |
| xsearch := search | |
| if xn.nodeType > static { | |
| p := -1 | |
| if xn.nodeType < wildcard { | |
| // To match or not match . in path | |
| chars := "/" | |
| if xn.endinglabel == '.' { | |
| chars += "." | |
| } | |
| p = stringsIndexAny(xsearch, chars) | |
| } | |
| if p < 0 { | |
| p = len(xsearch) | |
| } | |
| if xn.nodeType == wildcard { | |
| c.addParam("*", xsearch) | |
| } else { | |
| c.addParam(xn.pattern[1:], xsearch[:p]) | |
| } | |
| xsearch = xsearch[p:] | |
| } else if strings.HasPrefix(xsearch, xn.pattern) { | |
| xsearch = xsearch[len(xn.pattern):] | |
| } else { | |
| continue | |
| } | |
| if len(xsearch) == 0 && xn.isLeaf() { | |
| return xn, c | |
| } | |
| root = xn | |
| search = xsearch | |
| continue LOOP // Search for next node (xn) | |
| } | |
| break | |
| } | |
| return nil, c | |
| } | |
| func (n *node) findEdge(ndtype nodeType, label byte) *node { | |
| nds := n.children[ndtype] | |
| l := len(nds) | |
| idx := 0 | |
| LOOP: | |
| for ; idx < l; idx++ { | |
| switch ndtype { | |
| case static: | |
| if nds[idx].label >= label { | |
| break LOOP | |
| } | |
| default: | |
| break LOOP | |
| } | |
| } | |
| if idx >= l { | |
| return nil | |
| } | |
| node := nds[idx] | |
| if node.nodeType == static && node.label == label { | |
| return node | |
| } else if node.nodeType > static { | |
| return node | |
| } | |
| return nil | |
| } | |
| func (n *node) isEdge() bool { | |
| return n.label != 0 | |
| } | |
| func (n *node) longestPrefix(pattern string) int { | |
| return longestPrefix(n.pattern, pattern) | |
| } | |
| func (n *node) addChild(child *node) { | |
| search := child.pattern | |
| pos := stringsIndexAny(search, ":*") | |
| ndtype := static | |
| if pos >= 0 { | |
| switch search[pos] { | |
| case ':': | |
| ndtype = param | |
| case '*': | |
| ndtype = wildcard | |
| } | |
| } | |
| switch { | |
| case pos == 0: // Pattern starts with wildcard | |
| l := len(search) | |
| handler := child.handler | |
| child.nodeType = ndtype | |
| if ndtype == wildcard { | |
| pos = -1 | |
| } else { | |
| pos = stringsIndexAny(search, "./") | |
| } | |
| if pos < 0 { | |
| pos = l | |
| } else { | |
| child.endinglabel = search[pos] | |
| } | |
| child.pattern = search[:pos] | |
| if pos != l { | |
| child.handler = nil | |
| search = search[pos:] | |
| subchild := &node{ | |
| label: search[0], | |
| pattern: search, | |
| nodeType: static, | |
| handler: handler, | |
| } | |
| child.addChild(subchild) | |
| } | |
| case pos > 0: // Pattern has a wildcard parameter | |
| handler := child.handler | |
| child.nodeType = static | |
| child.pattern = search[:pos] | |
| child.handler = nil | |
| search = search[pos:] | |
| subchild := &node{ | |
| label: search[0], | |
| nodeType: ndtype, | |
| pattern: search, | |
| handler: handler, | |
| } | |
| child.addChild(subchild) | |
| default: // all static | |
| child.nodeType = ndtype | |
| } | |
| n.children[child.nodeType] = append(n.children[child.nodeType], child) | |
| n.children[child.nodeType].Sort() | |
| } | |
| type nodes []*node | |
| func (ns nodes) Len() int { return len(ns) } | |
| func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label } | |
| func (ns nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } | |
| func (ns nodes) Sort() { sort.Sort(ns) } | |
| type typesToNodes [wildcard + 1]nodes |