Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 39 additions & 14 deletions router.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,15 @@ type node struct {
prefix string
originalPath string
staticChildren children
paramsCount int
kind kind
label byte
isLeaf bool
isHandler bool
// scLabels holds the first byte (label) of each staticChildren entry in the
// same order. Scanning this contiguous byte slice during routing is more
// cache-friendly than dereferencing each *node to read its label.
scLabels []byte
paramsCount int
kind kind
label byte
isLeaf bool
isHandler bool
}

type kind uint8
Expand Down Expand Up @@ -406,13 +410,14 @@ func (r *DefaultRouter) Remove(method string, path string) error {
}
}
parent.staticChildren = append(parent.staticChildren[:index], parent.staticChildren[index+1:]...)
parent.scLabels = append(parent.scLabels[:index], parent.scLabels[index+1:]...)
case paramKind:
parent.paramChild = nil
case anyKind:
parent.anyChild = nil
}

parent.isLeaf = parent.anyChild == nil && parent.paramChild == nil && len(parent.staticChildren) == 0
parent.refreshLeaf()
if !parent.isLeaf || parent.isHandler {
break
}
Expand Down Expand Up @@ -572,7 +577,7 @@ func (r *DefaultRouter) insert(t kind, path string, method string, ri routeMetho
currentNode.paramsCount = len(ri.Parameters)
currentNode.originalPath = ri.Path
}
currentNode.isLeaf = currentNode.staticChildren == nil && currentNode.paramChild == nil && currentNode.anyChild == nil
currentNode.refreshLeaf()
} else if lcpLen < prefixLen {
// Split node into two before we insert new node.
// This happens when we are inserting path that is submatch of any existing inserted paths.
Expand Down Expand Up @@ -607,6 +612,7 @@ func (r *DefaultRouter) insert(t kind, path string, method string, ri routeMetho
currentNode.label = currentNode.prefix[0]
currentNode.prefix = currentNode.prefix[:lcpLen]
currentNode.staticChildren = nil
currentNode.scLabels = nil
currentNode.methods = new(routeMethods)
currentNode.originalPath = ""
currentNode.paramsCount = 0
Expand Down Expand Up @@ -636,7 +642,7 @@ func (r *DefaultRouter) insert(t kind, path string, method string, ri routeMetho
// Only Static children could reach here
currentNode.addStaticChild(n)
}
currentNode.isLeaf = currentNode.staticChildren == nil && currentNode.paramChild == nil && currentNode.anyChild == nil
currentNode.refreshLeaf()
} else if lcpLen < searchLen {
search = search[lcpLen:]
c := currentNode.findChildWithLabel(search[0])
Expand All @@ -659,7 +665,7 @@ func (r *DefaultRouter) insert(t kind, path string, method string, ri routeMetho
case anyKind:
currentNode.anyChild = n
}
currentNode.isLeaf = currentNode.staticChildren == nil && currentNode.paramChild == nil && currentNode.anyChild == nil
currentNode.refreshLeaf()
} else {
// Node already exists
if ri.handler != nil {
Expand All @@ -683,30 +689,49 @@ func newNode(
paramChildren,
anyChildren *node,
) *node {
var scLabels []byte
if len(sc) > 0 {
scLabels = make([]byte, len(sc))
for i, c := range sc {
scLabels[i] = c.label
}
}
return &node{
kind: t,
label: pre[0],
prefix: pre,
parent: p,
staticChildren: sc,
scLabels: scLabels,
originalPath: ppath,
paramsCount: paramsCount,
methods: mh,
paramChild: paramChildren,
anyChild: anyChildren,
isLeaf: sc == nil && paramChildren == nil && anyChildren == nil,
isHandler: mh.isHandler(),
// len() (not == nil) so an empty-but-non-nil sc — e.g. a slice from a
// prior Remove that was spliced down to length 0 — is still treated as
// no children, matching refreshLeaf.
isLeaf: len(sc) == 0 && paramChildren == nil && anyChildren == nil,
isHandler: mh.isHandler(),
}
}

// refreshLeaf recomputes whether the node is a leaf (has no children of any
// kind). len() is used for staticChildren so it stays correct whether the slice
// is nil or an emptied-but-non-nil slice left behind after a removal.
func (n *node) refreshLeaf() {
n.isLeaf = len(n.staticChildren) == 0 && n.paramChild == nil && n.anyChild == nil
}

func (n *node) addStaticChild(c *node) {
n.staticChildren = append(n.staticChildren, c)
n.scLabels = append(n.scLabels, c.label)
}

func (n *node) findStaticChild(l byte) *node {
for _, c := range n.staticChildren {
if c.label == l {
return c
for i, cl := range n.scLabels {
if cl == l {
return n.staticChildren[i]
}
}
return nil
Expand Down
Loading