type node struct
在router.go中可以看到对node的使用
type Router struct {
trees map[string]*node
}
map中的key是不同的method,*node就是不同method的树的根节点
直接看结构体定义
type node struct {
path string // 路径,比如`s`、`earch`、`up`
wildChild bool // 标明当前节点的子是否为参数节点
nType nodeType //
maxParams uint8 // 自己以及所有子孙节点,path中的最大参数数量
priority uint32 // 优先级,是当前节点的子孙节点数量+1,在介绍中有提到过
indices string // 存放所有子节点的首字母,比如`eu`
children []*node //
handle Handle // 处理程序
}
nodetype包含四种类型
type nodeType uint8
const (
static nodeType = iota // default,譬如search与sup中的s节点
root // 每个方法的根节点
param // 其他的节点
catchAll // 存在*name的节点
)
建树过程
建树主要涉及
node.addRoute()
以及node.insertChild()
两个函数
node.addRoute()
回顾一下,注册路由的函数是Router.Handle()
,该函数调用node.addRoute()
,其接收者是某个method的root节点。
// addRoute 向tree中增加节点
// Not concurrency-safe!
func (n *node) addRoute(path string, handle Handle) {
fullPath := path
n.priority++ // 根节点的权重+1
numParams := countParams(path) // 计算路径中的参数个数
// non-empty tree
if len(n.path) > 0 || len(n.children) > 0 {
walk: // 循环的标记
for {
// Update maxParams of the current node
if numParams > n.maxParams {
n.maxParams = numParams
}
// 查找最长公共前缀
// This also implies that the common prefix contains no ':' or '*'
// since the existing key can't contain those chars.
i := 0
max := min(len(path), len(n.path)) // 最长不会超过最短path的长度
for i < max && path[i] == n.path[i] {
i++
}
// Split edge
// 如果公共部分比当前节点的path要短,如sup与search
// 需要将原来的node切分成公共前缀node和后续的node两个部分
if i < len(n.path) {
child := node{
path: n.path[i:],
wildChild: n.wildChild,
nType: static,
indices: n.indices,
children: n.children,
handle: n.handle,
priority: n.priority - 1, // 存疑,不知道为什么要-1
}
// Update maxParams (max of all children)
for i := range child.children {
if child.children[i].maxParams > child.maxParams {
child.maxParams = child.children[i].maxParams
}
}
n.children = []*node{&child}
// []byte for proper unicode char conversion, see #65
n.indices = string([]byte{n.path[i]})
n.path = path[:i]
n.handle = nil // 由于已经将handle给了子节点,当前路径中不含通配符
// 很明显的,如果有正常的子节点,就不可能再有`:`或者`*`,
n.wildChild = false
}
// Make new node a child of this node
// 如果公共部分比新的path要短
// 由于是先处理的n节点,所以当前节点中存放的就是公共前缀
// 所以只需要将path的非公共部分存放到一个新的节点中
if i < len(path) {
path = path[i:]
// 这个条件判断,当前路径是否有名字参数
// 也可以直接理解为,没有经过上一个if的处理
// 因为上一个if将n.wildChild设为了false
// 所以也意味着,公共前缀的长度>=len(n.path)
if n.wildChild {
n = n.children[0]
n.priority++
// Update maxParams of the child node
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)] &&
// Adding a child to a catchAll is not possible
n.nType != catchAll &&
// Check for longer wildcard, e.g. :name and :names
(len(n.path) >= len(path) || path[len(n.path)] == '/') {
continue walk
} else {
// Wildcard conflict
var pathSeg string
if n.nType == catchAll {
pathSeg = path
} else {
pathSeg = strings.SplitN(path, "/", 2)[0]
}
prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
panic("'" + pathSeg +
"' in new path '" + fullPath +
"' conflicts with existing wildcard '" + n.path +
"' in existing prefix '" + prefix +
"'")
}
}
c := path[0]
// slash after param
if n.nType == param && c == '/' && len(n.children) == 1 {
n = n.children[0]
n.priority++
continue walk
}
// Check if a child with the next path byte exists
for i := 0; i < len(n.indices); i++ {
if c == n.indices[i] {
i = n.incrementChildPrio(i)
n = n.children[i]
continue walk
}
}
// Otherwise insert it
if c != ':' && c != '*' {
// []byte for proper unicode char conversion, see #65
n.indices += string([]byte{c})
child := &node{
maxParams: numParams,
}
n.children = append(n.children, child)
n.incrementChildPrio(len(n.indices) - 1)
n = child
}
n.insertChild(numParams, path, fullPath, handle)
return
} else if i == len(path) { // Make node a (in-path) leaf
// 新注册的路由路径与当前节点路径相同
// 想要注册的路由是否已有handler
if n.handle != nil {
panic("a handle is already registered for path '" + fullPath + "'")
}
n.handle = handle
}
return
}
} else { // Empty tree
n.insertChild(numParams, path, fullPath, handle) //空树则给该树插入子节点
n.nType = root // 标明当前节点类型
}
}
node.insertChild()
func (n *node) insertChild(numParams uint8, path, fullPath string, handle Handle) {
var offset int // 用于记录目前已经处理了path中的长度
// find prefix until first wildcard (beginning with ':'' or '*'')
// 如果存在`:`或者`*`,需要对节点进行特殊处理
for i, max := 0, len(path); numParams > 0; i++ {
c := path[i]
if c != ':' && c != '*' {
continue
}
// find wildcard end (either '/' or path end)
// 找到`:`或`*`以后,找到其结尾
end := i + 1
for end < max && path[end] != '/' {
switch path[end] {
// the wildcard name must not contain ':' and '*'
case ':', '*':
panic("only one wildcard per path segment is allowed, has: '" +
path[i:] + "' in path '" + fullPath + "'")
default:
end++
}
}
// 如果存在子节点,则在此插入通配符将导致子节点无法访问
// 比如存在`/path1/path2`,则无法插入`/path1/:post`
if len(n.children) > 0 {
panic("wildcard route '" + path[i:end] +
"' conflicts with existing children in path '" + fullPath + "'")
}
// check if the wildcard has a name
// `:`和`*`是需要有名字的
if end-i < 2 {
panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
}
if c == ':' { // param
// 将`:`之前的分割出来
if i > 0 {
n.path = path[offset:i]
offset = i
}
child := &node{
nType: param,
maxParams: numParams,
}
n.children = []*node{child}
n.wildChild = true
n = child // 移动到child节点,准备处理剩下的path
n.priority++
numParams-- // 处理掉一个通配符,剩下的路径中的参数个数--
// if the path doesn't end with the wildcard, then there
// will be another non-wildcard subpath starting with '/'
// 如果通配符项不是剩余path的全部路径,则需要将通配符这一节
// 单独做成一个节点
if end < max {
n.path = path[offset:end]
offset = end
child := &node{
maxParams: numParams,
priority: 1,
}
n.children = []*node{child}
n = child
}
} else { // catchAll
if end != max || 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[offset:i]
// first node: catchAll node with empty path
child := &node{
wildChild: true,
nType: catchAll,
maxParams: 1,
}
// 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,
handle: handle,
priority: 1,
}
n.children = []*node{child}
return
}
}
// insert remaining path part and handle to the leaf
n.path = path[offset:] // 节点中的path只保留非公共部分
n.handle = handle
}
其他函数
func countParams()
用于计算一个路径中有多少占位参数(:name
和*name
)
func countParams(path string) uint8 {
var n uint
for i := 0; i < len(path); i++ {
if path[i] != ':' && path[i] != '*' {
continue
}
n++
}
if n >= uint(maxParamCount) {
return maxParamCount
}
return uint8(n)
}
// var maxParamCount uint8 = ^uint8(0) 也就是255
相关内容
- #65,关于string的类型转换问题