From 3d714b5ed58cfdfd5872ddd3654d171b09bb02d3 Mon Sep 17 00:00:00 2001 From: unclejack Date: Mon, 1 Aug 2016 12:21:19 +0300 Subject: [PATCH] vendor.sh: bump go-patricia to 2.2.4 to fix leaks Signed-off-by: Cristian Staretu --- hack/vendor.sh | 2 +- .../tchap/go-patricia/patricia/children.go | 143 ++++++++++++++---- .../tchap/go-patricia/patricia/patricia.go | 139 ++++++++++++++++- 3 files changed, 246 insertions(+), 38 deletions(-) diff --git a/hack/vendor.sh b/hack/vendor.sh index 4b24d7b913..6396ffa9d3 100755 --- a/hack/vendor.sh +++ b/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 diff --git a/vendor/src/github.com/tchap/go-patricia/patricia/children.go b/vendor/src/github.com/tchap/go-patricia/patricia/children.go index a204b0c8a9..ef1f6142d4 100644 --- a/vendor/src/github.com/tchap/go-patricia/patricia/children.go +++ b/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,7 +67,26 @@ func (list *sparseChildList) add(child *Trie) childList { return newDenseChildList(list, child) } +func (list *sparseChildList) remove(b byte) { + for i, node := range list.children { + if node.prefix[0] == b { + 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) 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] == b { @@ -71,18 +96,6 @@ func (list *sparseChildList) replace(b byte, child *Trie) { } } -func (list *sparseChildList) remove(child *Trie) { - for i, node := range list.children { - if node.prefix[0] == child.prefix[0] { - list.children = append(list.children[:i], list.children[i+1:]...) - return - } - } - - // This is not supposed to be reached. - panic("removing non-existent child") -} - func (list *sparseChildList) next(b byte) *Trie { for _, child := range list.children { if child.prefix[0] == b { @@ -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 +} diff --git a/vendor/src/github.com/tchap/go-patricia/patricia/patricia.go b/vendor/src/github.com/tchap/go-patricia/patricia/patricia.go index a8c3786b62..a1fc53d5db 100644 --- a/vendor/src/github.com/tchap/go-patricia/patricia/patricia.go +++ b/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 (