Update go-patricia to 2.1.0

This includes a fix for the minor v2 API change introduced by 341a37095f. 👍

Signed-off-by: Andrew "Tianon" Page <admwiggin@gmail.com>
This commit is contained in:
Tianon Gravi 2015-05-02 23:25:57 -06:00
parent 0ad6f90127
commit b447fef7ec
8 changed files with 108 additions and 45 deletions

View File

@ -45,7 +45,7 @@ clone git github.com/gorilla/context 14f550f51a
clone git github.com/gorilla/mux e444e69cbd clone git github.com/gorilla/mux e444e69cbd
clone git github.com/tchap/go-patricia v1.0.1 clone git github.com/tchap/go-patricia v2.1.0
clone hg code.google.com/p/go.net 84a4013f96e0 clone hg code.google.com/p/go.net 84a4013f96e0

View File

@ -14,12 +14,6 @@ var (
ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix") ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix")
) )
func init() {
// Change patricia max prefix per node length,
// because our len(ID) always 64
patricia.MaxPrefixPerNode = 64
}
// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes. // TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
// This is used to retrieve image and container IDs by more convenient shorthand prefixes. // This is used to retrieve image and container IDs by more convenient shorthand prefixes.
type TruncIndex struct { type TruncIndex struct {
@ -31,8 +25,11 @@ type TruncIndex struct {
// NewTruncIndex creates a new TruncIndex and initializes with a list of IDs // NewTruncIndex creates a new TruncIndex and initializes with a list of IDs
func NewTruncIndex(ids []string) (idx *TruncIndex) { func NewTruncIndex(ids []string) (idx *TruncIndex) {
idx = &TruncIndex{ idx = &TruncIndex{
ids: make(map[string]struct{}), ids: make(map[string]struct{}),
trie: patricia.NewTrie(),
// Change patricia max prefix per node length,
// because our len(ID) always 64
trie: patricia.NewTrie(patricia.MaxPrefixPerNode(64)),
} }
for _, id := range ids { for _, id := range ids {
idx.addID(id) idx.addID(id)

View File

@ -50,9 +50,12 @@ printItem := func(prefix patricia.Prefix, item patricia.Item) error {
return nil return nil
} }
// Create a new tree. // Create a new default trie (using the default parameter values).
trie := NewTrie() trie := NewTrie()
// Create a new custom trie.
trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10))
// Insert some items. // Insert some items.
trie.Insert(Prefix("Pepa Novak"), 1) trie.Insert(Prefix("Pepa Novak"), 1)
trie.Insert(Prefix("Pepa Sindelar"), 2) trie.Insert(Prefix("Pepa Sindelar"), 2)
@ -67,12 +70,12 @@ key = Prefix("Karel")
fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key)) fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
// Anybody called "Karel" here? true // Anybody called "Karel" here? true
// Walk the tree. // Walk the tree in alphabetical order.
trie.Visit(printItem) trie.Visit(printItem)
// "Karel Hynek Macha": 4
// "Karel Macha": 3
// "Pepa Novak": 1 // "Pepa Novak": 1
// "Pepa Sindelar": 2 // "Pepa Sindelar": 2
// "Karel Macha": 3
// "Karel Hynek Macha": 4
// Walk a subtree. // Walk a subtree.
trie.VisitSubtree(Prefix("Pepa"), printItem) trie.VisitSubtree(Prefix("Pepa"), printItem)
@ -96,8 +99,8 @@ trie.Delete(Prefix("Karel Macha"))
// Walk again. // Walk again.
trie.Visit(printItem) trie.Visit(printItem)
// "Pepa Sindelar": 2
// "Karel Hynek Macha": 10 // "Karel Hynek Macha": 10
// "Pepa Sindelar": 2
// Delete a subtree. // Delete a subtree.
trie.DeleteSubtree(Prefix("Pepa")) trie.DeleteSubtree(Prefix("Pepa"))

View File

@ -5,11 +5,7 @@
package patricia package patricia
// Max prefix length that is kept in a single trie node. import "sort"
var MaxPrefixPerNode = 10
// Max children to keep in a node in the sparse mode.
const MaxChildrenPerSparseNode = 8
type childList interface { type childList interface {
length() int length() int
@ -21,13 +17,28 @@ type childList interface {
walk(prefix *Prefix, visitor VisitorFunc) error walk(prefix *Prefix, visitor VisitorFunc) error
} }
type sparseChildList struct { type tries []*Trie
children []*Trie
func (t tries) Len() int {
return len(t)
} }
func newSparseChildList() childList { func (t tries) Less(i, j int) bool {
strings := sort.StringSlice{string(t[i].prefix), string(t[j].prefix)}
return strings.Less(0, 1)
}
func (t tries) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
type sparseChildList struct {
children tries
}
func newSparseChildList(maxChildrenPerSparseNode int) childList {
return &sparseChildList{ return &sparseChildList{
children: make([]*Trie, 0, MaxChildrenPerSparseNode), children: make(tries, 0, DefaultMaxChildrenPerSparseNode),
} }
} }
@ -82,6 +93,9 @@ func (list *sparseChildList) next(b byte) *Trie {
} }
func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error { func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
sort.Sort(list.children)
for _, child := range list.children { for _, child := range list.children {
*prefix = append(*prefix, child.prefix...) *prefix = append(*prefix, child.prefix...)
if child.item != nil { if child.item != nil {

View File

@ -13,6 +13,11 @@ import (
// Trie // Trie
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const (
DefaultMaxPrefixPerNode = 10
DefaultMaxChildrenPerSparseNode = 8
)
type ( type (
Prefix []byte Prefix []byte
Item interface{} Item interface{}
@ -27,15 +32,44 @@ type Trie struct {
prefix Prefix prefix Prefix
item Item item Item
maxPrefixPerNode int
maxChildrenPerSparseNode int
children childList children childList
} }
// Public API ------------------------------------------------------------------ // Public API ------------------------------------------------------------------
type Option func(*Trie)
// Trie constructor. // Trie constructor.
func NewTrie() *Trie { func NewTrie(options ...Option) *Trie {
return &Trie{ trie := &Trie{}
children: newSparseChildList(),
for _, opt := range options {
opt(trie)
}
if trie.maxPrefixPerNode <= 0 {
trie.maxPrefixPerNode = DefaultMaxPrefixPerNode
}
if trie.maxChildrenPerSparseNode <= 0 {
trie.maxChildrenPerSparseNode = DefaultMaxChildrenPerSparseNode
}
trie.children = newSparseChildList(trie.maxChildrenPerSparseNode)
return trie
}
func MaxPrefixPerNode(value int) Option {
return func(trie *Trie) {
trie.maxPrefixPerNode = value
}
}
func MaxChildrenPerSparseNode(value int) Option {
return func(trie *Trie) {
trie.maxChildrenPerSparseNode = value
} }
} }
@ -85,7 +119,8 @@ func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
return return
} }
// Visit calls visitor on every node containing a non-nil item. // Visit calls visitor on every node containing a non-nil item
// in alphabetical order.
// //
// If an error is returned from visitor, the function stops visiting the tree // If an error is returned from visitor, the function stops visiting the tree
// and returns that error, unless it is a special error - SkipSubtree. In that // and returns that error, unless it is a special error - SkipSubtree. In that
@ -233,7 +268,7 @@ func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
// If we are in the root of the trie, reset the trie. // If we are in the root of the trie, reset the trie.
if parent == nil { if parent == nil {
root.prefix = nil root.prefix = nil
root.children = newSparseChildList() root.children = newSparseChildList(trie.maxPrefixPerNode)
return true return true
} }
@ -257,12 +292,12 @@ func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
) )
if node.prefix == nil { if node.prefix == nil {
if len(key) <= MaxPrefixPerNode { if len(key) <= trie.maxPrefixPerNode {
node.prefix = key node.prefix = key
goto InsertItem goto InsertItem
} }
node.prefix = key[:MaxPrefixPerNode] node.prefix = key[:trie.maxPrefixPerNode]
key = key[MaxPrefixPerNode:] key = key[trie.maxPrefixPerNode:]
goto AppendChild goto AppendChild
} }
@ -306,14 +341,14 @@ AppendChild:
// This loop starts with empty node.prefix that needs to be filled. // This loop starts with empty node.prefix that needs to be filled.
for len(key) != 0 { for len(key) != 0 {
child := NewTrie() child := NewTrie()
if len(key) <= MaxPrefixPerNode { if len(key) <= trie.maxPrefixPerNode {
child.prefix = key child.prefix = key
node.children = node.children.add(child) node.children = node.children.add(child)
node = child node = child
goto InsertItem goto InsertItem
} else { } else {
child.prefix = key[:MaxPrefixPerNode] child.prefix = key[:trie.maxPrefixPerNode]
key = key[MaxPrefixPerNode:] key = key[trie.maxPrefixPerNode:]
node.children = node.children.add(child) node.children = node.children.add(child)
node = child node = child
} }
@ -344,7 +379,7 @@ func (trie *Trie) compact() *Trie {
} }
// Make sure the combined prefixes fit into a single node. // Make sure the combined prefixes fit into a single node.
if len(trie.prefix)+len(child.prefix) > MaxPrefixPerNode { if len(trie.prefix)+len(child.prefix) > trie.maxPrefixPerNode {
return trie return trie
} }

View File

@ -55,7 +55,7 @@ func TestTrie_InsertDensePreceeding(t *testing.T) {
trie := NewTrie() trie := NewTrie()
start := byte(70) start := byte(70)
// create a dense node // create a dense node
for i := byte(0); i <= MaxChildrenPerSparseNode; i++ { for i := byte(0); i <= DefaultMaxChildrenPerSparseNode; i++ {
if !trie.Insert(Prefix([]byte{start + i}), true) { if !trie.Insert(Prefix([]byte{start + i}), true) {
t.Errorf("insert failed, prefix=%v", start+i) t.Errorf("insert failed, prefix=%v", start+i)
} }

View File

@ -300,10 +300,10 @@ func TestTrie_VisitReturnError(t *testing.T) {
someErr := errors.New("Something exploded") someErr := errors.New("Something exploded")
if err := trie.Visit(func(prefix Prefix, item Item) error { if err := trie.Visit(func(prefix Prefix, item Item) error {
t.Logf("VISITING prefix=%q, item=%v", prefix, item) t.Logf("VISITING prefix=%q, item=%v", prefix, item)
if item.(int) == 0 { if item.(int) == 3 {
return someErr return someErr
} }
if item.(int) != 0 { if item.(int) != 3 {
t.Errorf("Unexpected prefix encountered, %q", prefix) t.Errorf("Unexpected prefix encountered, %q", prefix)
} }
return nil return nil
@ -598,10 +598,10 @@ func ExampleTrie() {
// Walk the tree. // Walk the tree.
trie.Visit(printItem) trie.Visit(printItem)
// "Karel Hynek Macha": 4
// "Karel Macha": 3
// "Pepa Novak": 1 // "Pepa Novak": 1
// "Pepa Sindelar": 2 // "Pepa Sindelar": 2
// "Karel Macha": 3
// "Karel Hynek Macha": 4
// Walk a subtree. // Walk a subtree.
trie.VisitSubtree(Prefix("Pepa"), printItem) trie.VisitSubtree(Prefix("Pepa"), printItem)
@ -625,8 +625,8 @@ func ExampleTrie() {
// Walk again. // Walk again.
trie.Visit(printItem) trie.Visit(printItem)
// "Pepa Sindelar": 2
// "Karel Hynek Macha": 10 // "Karel Hynek Macha": 10
// "Pepa Sindelar": 2
// Delete a subtree. // Delete a subtree.
trie.DeleteSubtree(Prefix("Pepa")) trie.DeleteSubtree(Prefix("Pepa"))
@ -638,16 +638,16 @@ func ExampleTrie() {
// Output: // Output:
// "Pepa Novak" present? true // "Pepa Novak" present? true
// Anybody called "Karel" here? true // Anybody called "Karel" here? true
// "Pepa Novak": 1
// "Pepa Sindelar": 2
// "Karel Macha": 3
// "Karel Hynek Macha": 4 // "Karel Hynek Macha": 4
// "Karel Macha": 3
// "Pepa Novak": 1
// "Pepa Sindelar": 2
// "Pepa Novak": 1 // "Pepa Novak": 1
// "Pepa Sindelar": 2 // "Pepa Sindelar": 2
// "Karel Hynek Macha": 10 // "Karel Hynek Macha": 10
// "Karel Hynek Macha": 10 // "Karel Hynek Macha": 10
// "Pepa Sindelar": 2
// "Karel Hynek Macha": 10 // "Karel Hynek Macha": 10
// "Pepa Sindelar": 2
// "Karel Hynek Macha": 10 // "Karel Hynek Macha": 10
} }

View File

@ -13,6 +13,20 @@ import (
// Tests ----------------------------------------------------------------------- // Tests -----------------------------------------------------------------------
func TestTrie_ConstructorOptions(t *testing.T) {
trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10))
if trie.maxPrefixPerNode != 16 {
t.Errorf("Unexpected trie.maxPrefixPerNode value, expected=%v, got=%v",
16, trie.maxPrefixPerNode)
}
if trie.maxChildrenPerSparseNode != 10 {
t.Errorf("Unexpected trie.maxChildrenPerSparseNode value, expected=%v, got=%v",
10, trie.maxChildrenPerSparseNode)
}
}
func TestTrie_GetNonexistentPrefix(t *testing.T) { func TestTrie_GetNonexistentPrefix(t *testing.T) {
trie := NewTrie() trie := NewTrie()