type node struct

在router.go中可以看到对node的使用

  1. type Router struct {
  2. trees map[string]*node
  3. }

map中的key是不同的method,*node就是不同method的树的根节点

直接看结构体定义

  1. type node struct {
  2. path string // 路径,比如`s`、`earch`、`up`
  3. wildChild bool // 标明当前节点的子是否为参数节点
  4. nType nodeType //
  5. maxParams uint8 // 自己以及所有子孙节点,path中的最大参数数量
  6. priority uint32 // 优先级,是当前节点的子孙节点数量+1,在介绍中有提到过
  7. indices string // 存放所有子节点的首字母,比如`eu`
  8. children []*node //
  9. handle Handle // 处理程序
  10. }

nodetype包含四种类型

  1. type nodeType uint8
  2. const (
  3. static nodeType = iota // default,譬如search与sup中的s节点
  4. root // 每个方法的根节点
  5. param // 其他的节点
  6. catchAll // 存在*name的节点
  7. )

建树过程

建树主要涉及node.addRoute()以及node.insertChild()两个函数

node.addRoute()

回顾一下,注册路由的函数是Router.Handle(),该函数调用node.addRoute(),其接收者是某个method的root节点。

  1. // addRoute 向tree中增加节点
  2. // Not concurrency-safe!
  3. func (n *node) addRoute(path string, handle Handle) {
  4. fullPath := path
  5. n.priority++ // 根节点的权重+1
  6. numParams := countParams(path) // 计算路径中的参数个数
  7. // non-empty tree
  8. if len(n.path) > 0 || len(n.children) > 0 {
  9. walk: // 循环的标记
  10. for {
  11. // Update maxParams of the current node
  12. if numParams > n.maxParams {
  13. n.maxParams = numParams
  14. }
  15. // 查找最长公共前缀
  16. // This also implies that the common prefix contains no ':' or '*'
  17. // since the existing key can't contain those chars.
  18. i := 0
  19. max := min(len(path), len(n.path)) // 最长不会超过最短path的长度
  20. for i < max && path[i] == n.path[i] {
  21. i++
  22. }
  23. // Split edge
  24. // 如果公共部分比当前节点的path要短,如sup与search
  25. // 需要将原来的node切分成公共前缀node和后续的node两个部分
  26. if i < len(n.path) {
  27. child := node{
  28. path: n.path[i:],
  29. wildChild: n.wildChild,
  30. nType: static,
  31. indices: n.indices,
  32. children: n.children,
  33. handle: n.handle,
  34. priority: n.priority - 1, // 存疑,不知道为什么要-1
  35. }
  36. // Update maxParams (max of all children)
  37. for i := range child.children {
  38. if child.children[i].maxParams > child.maxParams {
  39. child.maxParams = child.children[i].maxParams
  40. }
  41. }
  42. n.children = []*node{&child}
  43. // []byte for proper unicode char conversion, see #65
  44. n.indices = string([]byte{n.path[i]})
  45. n.path = path[:i]
  46. n.handle = nil // 由于已经将handle给了子节点,当前路径中不含通配符
  47. // 很明显的,如果有正常的子节点,就不可能再有`:`或者`*`,
  48. n.wildChild = false
  49. }
  50. // Make new node a child of this node
  51. // 如果公共部分比新的path要短
  52. // 由于是先处理的n节点,所以当前节点中存放的就是公共前缀
  53. // 所以只需要将path的非公共部分存放到一个新的节点中
  54. if i < len(path) {
  55. path = path[i:]
  56. // 这个条件判断,当前路径是否有名字参数
  57. // 也可以直接理解为,没有经过上一个if的处理
  58. // 因为上一个if将n.wildChild设为了false
  59. // 所以也意味着,公共前缀的长度>=len(n.path)
  60. if n.wildChild {
  61. n = n.children[0]
  62. n.priority++
  63. // Update maxParams of the child node
  64. if numParams > n.maxParams {
  65. n.maxParams = numParams
  66. }
  67. numParams--
  68. // Check if the wildcard matches
  69. if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
  70. // Adding a child to a catchAll is not possible
  71. n.nType != catchAll &&
  72. // Check for longer wildcard, e.g. :name and :names
  73. (len(n.path) >= len(path) || path[len(n.path)] == '/') {
  74. continue walk
  75. } else {
  76. // Wildcard conflict
  77. var pathSeg string
  78. if n.nType == catchAll {
  79. pathSeg = path
  80. } else {
  81. pathSeg = strings.SplitN(path, "/", 2)[0]
  82. }
  83. prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
  84. panic("'" + pathSeg +
  85. "' in new path '" + fullPath +
  86. "' conflicts with existing wildcard '" + n.path +
  87. "' in existing prefix '" + prefix +
  88. "'")
  89. }
  90. }
  91. c := path[0]
  92. // slash after param
  93. if n.nType == param && c == '/' && len(n.children) == 1 {
  94. n = n.children[0]
  95. n.priority++
  96. continue walk
  97. }
  98. // Check if a child with the next path byte exists
  99. for i := 0; i < len(n.indices); i++ {
  100. if c == n.indices[i] {
  101. i = n.incrementChildPrio(i)
  102. n = n.children[i]
  103. continue walk
  104. }
  105. }
  106. // Otherwise insert it
  107. if c != ':' && c != '*' {
  108. // []byte for proper unicode char conversion, see #65
  109. n.indices += string([]byte{c})
  110. child := &node{
  111. maxParams: numParams,
  112. }
  113. n.children = append(n.children, child)
  114. n.incrementChildPrio(len(n.indices) - 1)
  115. n = child
  116. }
  117. n.insertChild(numParams, path, fullPath, handle)
  118. return
  119. } else if i == len(path) { // Make node a (in-path) leaf
  120. // 新注册的路由路径与当前节点路径相同
  121. // 想要注册的路由是否已有handler
  122. if n.handle != nil {
  123. panic("a handle is already registered for path '" + fullPath + "'")
  124. }
  125. n.handle = handle
  126. }
  127. return
  128. }
  129. } else { // Empty tree
  130. n.insertChild(numParams, path, fullPath, handle) //空树则给该树插入子节点
  131. n.nType = root // 标明当前节点类型
  132. }
  133. }

node.insertChild()

  1. func (n *node) insertChild(numParams uint8, path, fullPath string, handle Handle) {
  2. var offset int // 用于记录目前已经处理了path中的长度
  3. // find prefix until first wildcard (beginning with ':'' or '*'')
  4. // 如果存在`:`或者`*`,需要对节点进行特殊处理
  5. for i, max := 0, len(path); numParams > 0; i++ {
  6. c := path[i]
  7. if c != ':' && c != '*' {
  8. continue
  9. }
  10. // find wildcard end (either '/' or path end)
  11. // 找到`:`或`*`以后,找到其结尾
  12. end := i + 1
  13. for end < max && path[end] != '/' {
  14. switch path[end] {
  15. // the wildcard name must not contain ':' and '*'
  16. case ':', '*':
  17. panic("only one wildcard per path segment is allowed, has: '" +
  18. path[i:] + "' in path '" + fullPath + "'")
  19. default:
  20. end++
  21. }
  22. }
  23. // 如果存在子节点,则在此插入通配符将导致子节点无法访问
  24. // 比如存在`/path1/path2`,则无法插入`/path1/:post`
  25. if len(n.children) > 0 {
  26. panic("wildcard route '" + path[i:end] +
  27. "' conflicts with existing children in path '" + fullPath + "'")
  28. }
  29. // check if the wildcard has a name
  30. // `:`和`*`是需要有名字的
  31. if end-i < 2 {
  32. panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
  33. }
  34. if c == ':' { // param
  35. // 将`:`之前的分割出来
  36. if i > 0 {
  37. n.path = path[offset:i]
  38. offset = i
  39. }
  40. child := &node{
  41. nType: param,
  42. maxParams: numParams,
  43. }
  44. n.children = []*node{child}
  45. n.wildChild = true
  46. n = child // 移动到child节点,准备处理剩下的path
  47. n.priority++
  48. numParams-- // 处理掉一个通配符,剩下的路径中的参数个数--
  49. // if the path doesn't end with the wildcard, then there
  50. // will be another non-wildcard subpath starting with '/'
  51. // 如果通配符项不是剩余path的全部路径,则需要将通配符这一节
  52. // 单独做成一个节点
  53. if end < max {
  54. n.path = path[offset:end]
  55. offset = end
  56. child := &node{
  57. maxParams: numParams,
  58. priority: 1,
  59. }
  60. n.children = []*node{child}
  61. n = child
  62. }
  63. } else { // catchAll
  64. if end != max || numParams > 1 {
  65. panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
  66. }
  67. if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
  68. panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
  69. }
  70. // currently fixed width 1 for '/'
  71. i--
  72. if path[i] != '/' {
  73. panic("no / before catch-all in path '" + fullPath + "'")
  74. }
  75. n.path = path[offset:i]
  76. // first node: catchAll node with empty path
  77. child := &node{
  78. wildChild: true,
  79. nType: catchAll,
  80. maxParams: 1,
  81. }
  82. // update maxParams of the parent node
  83. if n.maxParams < 1 {
  84. n.maxParams = 1
  85. }
  86. n.children = []*node{child}
  87. n.indices = string(path[i])
  88. n = child
  89. n.priority++
  90. // second node: node holding the variable
  91. child = &node{
  92. path: path[i:],
  93. nType: catchAll,
  94. maxParams: 1,
  95. handle: handle,
  96. priority: 1,
  97. }
  98. n.children = []*node{child}
  99. return
  100. }
  101. }
  102. // insert remaining path part and handle to the leaf
  103. n.path = path[offset:] // 节点中的path只保留非公共部分
  104. n.handle = handle
  105. }

其他函数

func countParams()

用于计算一个路径中有多少占位参数(:name*name

  1. func countParams(path string) uint8 {
  2. var n uint
  3. for i := 0; i < len(path); i++ {
  4. if path[i] != ':' && path[i] != '*' {
  5. continue
  6. }
  7. n++
  8. }
  9. if n >= uint(maxParamCount) {
  10. return maxParamCount
  11. }
  12. return uint8(n)
  13. }
  14. // var maxParamCount uint8 = ^uint8(0) 也就是255

相关内容

  • #65,关于string的类型转换问题