139 lines
2.7 KiB
Go
139 lines
2.7 KiB
Go
package engine
|
|
|
|
type pathPrefixIndex struct {
|
|
root pathPrefixNode
|
|
}
|
|
|
|
type pathPrefixNode struct {
|
|
children map[string]*pathPrefixNode
|
|
ids []int
|
|
minimumID int
|
|
hasMinimum bool
|
|
}
|
|
|
|
func (node *pathPrefixNode) includeID(id int) {
|
|
if !node.hasMinimum || id < node.minimumID {
|
|
node.minimumID = id
|
|
node.hasMinimum = true
|
|
}
|
|
}
|
|
|
|
func (index *pathPrefixIndex) insert(pointer string, id int) {
|
|
node := &index.root
|
|
node.includeID(id)
|
|
visitCanonicalPointerTokens(pointer, func(token string) bool {
|
|
if node.children == nil {
|
|
node.children = make(map[string]*pathPrefixNode)
|
|
}
|
|
child := node.children[token]
|
|
if child == nil {
|
|
child = &pathPrefixNode{}
|
|
node.children[token] = child
|
|
}
|
|
node = child
|
|
node.includeID(id)
|
|
return true
|
|
})
|
|
node.ids = append(node.ids, id)
|
|
}
|
|
|
|
func (index *pathPrefixIndex) visitAncestors(pointer string, visit func(int)) {
|
|
node := &index.root
|
|
for _, id := range node.ids {
|
|
visit(id)
|
|
}
|
|
visitCanonicalPointerTokens(pointer, func(token string) bool {
|
|
child := node.children[token]
|
|
if child == nil {
|
|
return false
|
|
}
|
|
node = child
|
|
for _, id := range node.ids {
|
|
visit(id)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (index *pathPrefixIndex) visitDescendants(pointer string, visit func(int)) {
|
|
root := index.nodeAt(pointer)
|
|
if root == nil {
|
|
return
|
|
}
|
|
var walk func(*pathPrefixNode)
|
|
walk = func(node *pathPrefixNode) {
|
|
for _, id := range node.ids {
|
|
visit(id)
|
|
}
|
|
for _, child := range node.children {
|
|
walk(child)
|
|
}
|
|
}
|
|
walk(root)
|
|
}
|
|
|
|
func (index *pathPrefixIndex) earliestIntersectingID(pointer string) (int, bool) {
|
|
bestID, found := 0, false
|
|
consider := func(id int) {
|
|
if !found || id < bestID {
|
|
bestID = id
|
|
found = true
|
|
}
|
|
}
|
|
node := &index.root
|
|
for _, id := range node.ids {
|
|
consider(id)
|
|
}
|
|
complete := visitCanonicalPointerTokens(pointer, func(token string) bool {
|
|
child := node.children[token]
|
|
if child == nil {
|
|
return false
|
|
}
|
|
node = child
|
|
for _, id := range node.ids {
|
|
consider(id)
|
|
}
|
|
return true
|
|
})
|
|
if complete && node.hasMinimum {
|
|
consider(node.minimumID)
|
|
}
|
|
return bestID, found
|
|
}
|
|
|
|
func (index *pathPrefixIndex) nodeAt(pointer string) *pathPrefixNode {
|
|
node := &index.root
|
|
complete := visitCanonicalPointerTokens(pointer, func(token string) bool {
|
|
child := node.children[token]
|
|
if child == nil {
|
|
return false
|
|
}
|
|
node = child
|
|
return true
|
|
})
|
|
if !complete {
|
|
return nil
|
|
}
|
|
return node
|
|
}
|
|
|
|
func visitCanonicalPointerTokens(pointer string, visit func(string) bool) bool {
|
|
if pointer == "" {
|
|
return true
|
|
}
|
|
start := 1
|
|
for {
|
|
end := start
|
|
for end < len(pointer) && pointer[end] != '/' {
|
|
end++
|
|
}
|
|
if !visit(pointer[start:end]) {
|
|
return false
|
|
}
|
|
if end == len(pointer) {
|
|
return true
|
|
}
|
|
start = end + 1
|
|
}
|
|
}
|