зеркало из https://github.com/microsoft/docker.git
Merge pull request #25283 from unclejack/bump_go_patricia_2.2.4
vendor.sh: bump go-patricia to 2.2.4 to fix leaks
This commit is contained in:
Коммит
ccbdc16b8e
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
|
|
Загрузка…
Ссылка в новой задаче