Explorar o código

Merge pull request #25283 from unclejack/bump_go_patricia_2.2.4

vendor.sh: bump go-patricia to 2.2.4 to fix leaks
Alexander Morozov %!s(int64=9) %!d(string=hai) anos
pai
achega
ccbdc16b8e

+ 1 - 1
hack/vendor.sh

@@ -53,7 +53,7 @@ clone git github.com/gorilla/mux e444e69cbd
 clone git github.com/kr/pty 5cf931ef8f
 clone git github.com/mattn/go-shellwords v1.0.0
 clone git github.com/mattn/go-sqlite3 v1.1.0
-clone git github.com/tchap/go-patricia v2.1.0
+clone git github.com/tchap/go-patricia v2.2.4
 clone git github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
 # forked golang.org/x/net package includes a patch for lazy loading trace templates
 clone git golang.org/x/net 2beffdc2e92c8a3027590f898fe88f69af48a3f8 https://github.com/tonistiigi/net.git

+ 109 - 28
vendor/src/github.com/tchap/go-patricia/patricia/children.go

@@ -5,16 +5,22 @@
 
 package patricia
 
-import "sort"
+import (
+	"fmt"
+	"io"
+	"sort"
+)
 
 type childList interface {
 	length() int
 	head() *Trie
 	add(child *Trie) childList
+	remove(b byte)
 	replace(b byte, child *Trie)
-	remove(child *Trie)
 	next(b byte) *Trie
 	walk(prefix *Prefix, visitor VisitorFunc) error
+	print(w io.Writer, indent int)
+	total() int
 }
 
 type tries []*Trie
@@ -38,7 +44,7 @@ type sparseChildList struct {
 
 func newSparseChildList(maxChildrenPerSparseNode int) childList {
 	return &sparseChildList{
-		children: make(tries, 0, DefaultMaxChildrenPerSparseNode),
+		children: make(tries, 0, maxChildrenPerSparseNode),
 	}
 }
 
@@ -61,26 +67,33 @@ func (list *sparseChildList) add(child *Trie) childList {
 	return newDenseChildList(list, child)
 }
 
-func (list *sparseChildList) replace(b byte, child *Trie) {
-	// Seek the child and replace it.
+func (list *sparseChildList) remove(b byte) {
 	for i, node := range list.children {
 		if node.prefix[0] == b {
-			list.children[i] = child
+			list.children, list.children[len(list.children)-1] =
+				append(list.children[:i], list.children[i+1:]...),
+				nil
 			return
 		}
 	}
+
+	// This is not supposed to be reached.
+	panic("removing non-existent child")
 }
 
-func (list *sparseChildList) remove(child *Trie) {
+func (list *sparseChildList) replace(b byte, child *Trie) {
+	// Make a consistency check.
+	if p0 := child.prefix[0]; p0 != b {
+		panic(fmt.Errorf("child prefix mismatch: %v != %v", p0, b))
+	}
+
+	// Seek the child and replace it.
 	for i, node := range list.children {
-		if node.prefix[0] == child.prefix[0] {
-			list.children = append(list.children[:i], list.children[i+1:]...)
+		if node.prefix[0] == b {
+			list.children[i] = child
 			return
 		}
 	}
-
-	// This is not supposed to be reached.
-	panic("removing non-existent child")
 }
 
 func (list *sparseChildList) next(b byte) *Trie {
@@ -120,10 +133,30 @@ func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
 	return nil
 }
 
+func (list *sparseChildList) total() int {
+	tot := 0
+	for _, child := range list.children {
+		if child != nil {
+			tot = tot + child.total()
+		}
+	}
+	return tot
+}
+
+func (list *sparseChildList) print(w io.Writer, indent int) {
+	for _, child := range list.children {
+		if child != nil {
+			child.print(w, indent)
+		}
+	}
+}
+
 type denseChildList struct {
-	min      int
-	max      int
-	children []*Trie
+	min         int
+	max         int
+	numChildren int
+	headIndex   int
+	children    []*Trie
 }
 
 func newDenseChildList(list *sparseChildList, child *Trie) childList {
@@ -155,57 +188,87 @@ func newDenseChildList(list *sparseChildList, child *Trie) childList {
 	}
 	children[int(child.prefix[0])-min] = child
 
-	return &denseChildList{min, max, children}
+	return &denseChildList{
+		min:         min,
+		max:         max,
+		numChildren: list.length() + 1,
+		headIndex:   0,
+		children:    children,
+	}
 }
 
 func (list *denseChildList) length() int {
-	return list.max - list.min + 1
+	return list.numChildren
 }
 
 func (list *denseChildList) head() *Trie {
-	return list.children[0]
+	return list.children[list.headIndex]
 }
 
 func (list *denseChildList) add(child *Trie) childList {
 	b := int(child.prefix[0])
+	var i int
 
 	switch {
 	case list.min <= b && b <= list.max:
 		if list.children[b-list.min] != nil {
 			panic("dense child list collision detected")
 		}
-		list.children[b-list.min] = child
+		i = b - list.min
+		list.children[i] = child
 
 	case b < list.min:
 		children := make([]*Trie, list.max-b+1)
-		children[0] = child
+		i = 0
+		children[i] = child
 		copy(children[list.min-b:], list.children)
 		list.children = children
 		list.min = b
 
 	default: // b > list.max
 		children := make([]*Trie, b-list.min+1)
-		children[b-list.min] = child
+		i = b - list.min
+		children[i] = child
 		copy(children, list.children)
 		list.children = children
 		list.max = b
 	}
 
+	list.numChildren++
+	if i < list.headIndex {
+		list.headIndex = i
+	}
 	return list
 }
 
-func (list *denseChildList) replace(b byte, child *Trie) {
-	list.children[int(b)-list.min] = nil
-	list.children[int(child.prefix[0])-list.min] = child
-}
-
-func (list *denseChildList) remove(child *Trie) {
-	i := int(child.prefix[0]) - list.min
+func (list *denseChildList) remove(b byte) {
+	i := int(b) - list.min
 	if list.children[i] == nil {
 		// This is not supposed to be reached.
 		panic("removing non-existent child")
 	}
+	list.numChildren--
 	list.children[i] = nil
+
+	// Update head index.
+	if i == list.headIndex {
+		for ; i < len(list.children); i++ {
+			if list.children[i] != nil {
+				list.headIndex = i
+				return
+			}
+		}
+	}
+}
+
+func (list *denseChildList) replace(b byte, child *Trie) {
+	// Make a consistency check.
+	if p0 := child.prefix[0]; p0 != b {
+		panic(fmt.Errorf("child prefix mismatch: %v != %v", p0, b))
+	}
+
+	// Replace the child.
+	list.children[int(b)-list.min] = child
 }
 
 func (list *denseChildList) next(b byte) *Trie {
@@ -242,3 +305,21 @@ func (list *denseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
 
 	return nil
 }
+
+func (list *denseChildList) print(w io.Writer, indent int) {
+	for _, child := range list.children {
+		if child != nil {
+			child.print(w, indent)
+		}
+	}
+}
+
+func (list *denseChildList) total() int {
+	tot := 0
+	for _, child := range list.children {
+		if child != nil {
+			tot = tot + child.total()
+		}
+	}
+	return tot
+}

+ 133 - 6
vendor/src/github.com/tchap/go-patricia/patricia/patricia.go

@@ -6,7 +6,11 @@
 package patricia
 
 import (
+	"bytes"
 	"errors"
+	"fmt"
+	"io"
+	"strings"
 )
 
 //------------------------------------------------------------------------------
@@ -130,6 +134,21 @@ func (trie *Trie) Visit(visitor VisitorFunc) error {
 	return trie.walk(nil, visitor)
 }
 
+func (trie *Trie) size() int {
+	n := 0
+
+	trie.walk(nil, func(prefix Prefix, item Item) error {
+		n++
+		return nil
+	})
+
+	return n
+}
+
+func (trie *Trie) total() int {
+	return 1 + trie.children.total()
+}
+
 // VisitSubtree works much like Visit, but it only visits nodes matching prefix.
 func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error {
 	// Nil prefix not allowed.
@@ -219,11 +238,17 @@ func (trie *Trie) Delete(key Prefix) (deleted bool) {
 	}
 
 	// Find the relevant node.
-	parent, node, _, leftover := trie.findSubtree(key)
-	if len(leftover) != 0 {
+	path, found, _ := trie.findSubtreePath(key)
+	if !found {
 		return false
 	}
 
+	node := path[len(path)-1]
+	var parent *Trie
+	if len(path) != 1 {
+		parent = path[len(path)-2]
+	}
+
 	// If the item is already set to nil, there is nothing to do.
 	if node.item == nil {
 		return false
@@ -232,7 +257,53 @@ func (trie *Trie) Delete(key Prefix) (deleted bool) {
 	// Delete the item.
 	node.item = nil
 
-	// Compact since that might be possible now.
+	// Initialise i before goto.
+	// Will be used later in a loop.
+	i := len(path) - 1
+
+	// In case there are some child nodes, we cannot drop the whole subtree.
+	// We can try to compact nodes, though.
+	if node.children.length() != 0 {
+		goto Compact
+	}
+
+	// In case we are at the root, just reset it and we are done.
+	if parent == nil {
+		node.reset()
+		return true
+	}
+
+	// We can drop a subtree.
+	// Find the first ancestor that has its value set or it has 2 or more child nodes.
+	// That will be the node where to drop the subtree at.
+	for ; i >= 0; i-- {
+		if current := path[i]; current.item != nil || current.children.length() >= 2 {
+			break
+		}
+	}
+
+	// Handle the case when there is no such node.
+	// In other words, we can reset the whole tree.
+	if i == -1 {
+		path[0].reset()
+		return true
+	}
+
+	// We can just remove the subtree here.
+	node = path[i]
+	if i == 0 {
+		parent = nil
+	} else {
+		parent = path[i-1]
+	}
+	// i+1 is always a valid index since i is never pointing to the last node.
+	// The loop above skips at least the last node since we are sure that the item
+	// is set to nil and it has no children, othewise we would be compacting instead.
+	node.children.remove(path[i+1].prefix[0])
+
+Compact:
+	// The node is set to the first non-empty ancestor,
+	// so try to compact since that might be possible now.
 	if compacted := node.compact(); compacted != node {
 		if parent == nil {
 			*node = *compacted
@@ -267,18 +338,26 @@ func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
 
 	// If we are in the root of the trie, reset the trie.
 	if parent == nil {
-		root.prefix = nil
-		root.children = newSparseChildList(trie.maxPrefixPerNode)
+		root.reset()
 		return true
 	}
 
 	// Otherwise remove the root node from its parent.
-	parent.children.remove(root)
+	parent.children.remove(root.prefix[0])
 	return true
 }
 
 // Internal helper methods -----------------------------------------------------
 
+func (trie *Trie) empty() bool {
+	return trie.item == nil && trie.children.length() == 0
+}
+
+func (trie *Trie) reset() {
+	trie.prefix = nil
+	trie.children = newSparseChildList(trie.maxPrefixPerNode)
+}
+
 func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
 	// Nil prefix not allowed.
 	if key == nil {
@@ -425,6 +504,43 @@ func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bo
 	}
 }
 
+func (trie *Trie) findSubtreePath(prefix Prefix) (path []*Trie, found bool, leftover Prefix) {
+	// Find the subtree matching prefix.
+	root := trie
+	var subtreePath []*Trie
+	for {
+		// Append the current root to the path.
+		subtreePath = append(subtreePath, root)
+
+		// Compute what part of prefix matches.
+		common := root.longestCommonPrefixLength(prefix)
+		prefix = prefix[common:]
+
+		// We used up the whole prefix, subtree found.
+		if len(prefix) == 0 {
+			path = subtreePath
+			found = true
+			leftover = root.prefix[common:]
+			return
+		}
+
+		// Partial match means that there is no subtree matching prefix.
+		if common < len(root.prefix) {
+			leftover = root.prefix[common:]
+			return
+		}
+
+		// There is some prefix left, move to the children.
+		child := root.children.next(prefix[0])
+		if child == nil {
+			// There is nowhere to continue, there is no subtree matching prefix.
+			return
+		}
+
+		root = child
+	}
+}
+
 func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error {
 	var prefix Prefix
 	// Allocate a bit more space for prefix at the beginning.
@@ -459,6 +575,17 @@ func (trie *Trie) longestCommonPrefixLength(prefix Prefix) (i int) {
 	return
 }
 
+func (trie *Trie) dump() string {
+	writer := &bytes.Buffer{}
+	trie.print(writer, 0)
+	return writer.String()
+}
+
+func (trie *Trie) print(writer io.Writer, indent int) {
+	fmt.Fprintf(writer, "%s%s %v\n", strings.Repeat(" ", indent), string(trie.prefix), trie.item)
+	trie.children.print(writer, indent+2)
+}
+
 // Errors ----------------------------------------------------------------------
 
 var (