瀏覽代碼

refactor TruncIndex to use a trie & vendor deps

Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)
unclejack 11 年之前
父節點
當前提交
e0dad30579

+ 1 - 7
daemon/daemon.go

@@ -182,11 +182,7 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool, con
 
 
 	// don't update the Suffixarray if we're starting up
 	// don't update the Suffixarray if we're starting up
 	// we'll waste time if we update it for every container
 	// we'll waste time if we update it for every container
-	if updateSuffixarray {
-		daemon.idIndex.Add(container.ID)
-	} else {
-		daemon.idIndex.AddWithoutSuffixarrayUpdate(container.ID)
-	}
+	daemon.idIndex.Add(container.ID)
 
 
 	// FIXME: if the container is supposed to be running but is not, auto restart it?
 	// FIXME: if the container is supposed to be running but is not, auto restart it?
 	//        if so, then we need to restart monitor and init a new lock
 	//        if so, then we need to restart monitor and init a new lock
@@ -377,8 +373,6 @@ func (daemon *Daemon) restore() error {
 		}
 		}
 	}
 	}
 
 
-	daemon.idIndex.UpdateSuffixarray()
-
 	for _, container := range containersToStart {
 	for _, container := range containersToStart {
 		utils.Debugf("Starting container %d", container.ID)
 		utils.Debugf("Starting container %d", container.ID)
 		if err := container.Start(); err != nil {
 		if err := container.Start(); err != nil {

+ 2 - 0
hack/vendor.sh

@@ -47,6 +47,8 @@ clone git github.com/gorilla/mux 136d54f81f
 
 
 clone git github.com/syndtr/gocapability 3c85049eae
 clone git github.com/syndtr/gocapability 3c85049eae
 
 
+clone git github.com/tchap/go-patricia v1.0.0 
+
 clone hg code.google.com/p/go.net 84a4013f96e0
 clone hg code.google.com/p/go.net 84a4013f96e0
 
 
 clone hg code.google.com/p/gosqlite 74691fb6f837
 clone hg code.google.com/p/gosqlite 74691fb6f837

+ 47 - 49
pkg/truncindex/truncindex.go

@@ -1,31 +1,34 @@
 package truncindex
 package truncindex
 
 
 import (
 import (
+	"errors"
 	"fmt"
 	"fmt"
-	"index/suffixarray"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
+
+	"github.com/tchap/go-patricia/patricia"
+)
+
+var (
+	ErrNoID = errors.New("prefix can't be empty")
 )
 )
 
 
 // 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 {
 	sync.RWMutex
 	sync.RWMutex
-	index *suffixarray.Index
-	ids   map[string]bool
-	bytes []byte
+	trie *patricia.Trie
+	ids  map[string]struct{}
 }
 }
 
 
 func NewTruncIndex(ids []string) (idx *TruncIndex) {
 func NewTruncIndex(ids []string) (idx *TruncIndex) {
 	idx = &TruncIndex{
 	idx = &TruncIndex{
-		ids:   make(map[string]bool),
-		bytes: []byte{' '},
+		ids:  make(map[string]struct{}),
+		trie: patricia.NewTrie(),
 	}
 	}
 	for _, id := range ids {
 	for _, id := range ids {
-		idx.ids[id] = true
-		idx.bytes = append(idx.bytes, []byte(id+" ")...)
+		idx.addId(id)
 	}
 	}
-	idx.index = suffixarray.New(idx.bytes)
 	return
 	return
 }
 }
 
 
@@ -33,11 +36,16 @@ func (idx *TruncIndex) addId(id string) error {
 	if strings.Contains(id, " ") {
 	if strings.Contains(id, " ") {
 		return fmt.Errorf("Illegal character: ' '")
 		return fmt.Errorf("Illegal character: ' '")
 	}
 	}
+	if id == "" {
+		return ErrNoID
+	}
 	if _, exists := idx.ids[id]; exists {
 	if _, exists := idx.ids[id]; exists {
-		return fmt.Errorf("Id already exists: %s", id)
+		return fmt.Errorf("Id already exists: '%s'", id)
+	}
+	idx.ids[id] = struct{}{}
+	if inserted := idx.trie.Insert(patricia.Prefix(id), struct{}{}); !inserted {
+		return fmt.Errorf("Failed to insert id: %s", id)
 	}
 	}
-	idx.ids[id] = true
-	idx.bytes = append(idx.bytes, []byte(id+" ")...)
 	return nil
 	return nil
 }
 }
 
 
@@ -47,56 +55,46 @@ func (idx *TruncIndex) Add(id string) error {
 	if err := idx.addId(id); err != nil {
 	if err := idx.addId(id); err != nil {
 		return err
 		return err
 	}
 	}
-	idx.index = suffixarray.New(idx.bytes)
 	return nil
 	return nil
 }
 }
 
 
-func (idx *TruncIndex) AddWithoutSuffixarrayUpdate(id string) error {
-	idx.Lock()
-	defer idx.Unlock()
-	return idx.addId(id)
-}
-
-func (idx *TruncIndex) UpdateSuffixarray() {
-	idx.Lock()
-	defer idx.Unlock()
-	idx.index = suffixarray.New(idx.bytes)
-}
-
 func (idx *TruncIndex) Delete(id string) error {
 func (idx *TruncIndex) Delete(id string) error {
 	idx.Lock()
 	idx.Lock()
 	defer idx.Unlock()
 	defer idx.Unlock()
-	if _, exists := idx.ids[id]; !exists {
-		return fmt.Errorf("No such id: %s", id)
-	}
-	before, after, err := idx.lookup(id)
-	if err != nil {
-		return err
+	if _, exists := idx.ids[id]; !exists || id == "" {
+		return fmt.Errorf("No such id: '%s'", id)
 	}
 	}
 	delete(idx.ids, id)
 	delete(idx.ids, id)
-	idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...)
-	idx.index = suffixarray.New(idx.bytes)
-	return nil
-}
-
-func (idx *TruncIndex) lookup(s string) (int, int, error) {
-	offsets := idx.index.Lookup([]byte(" "+s), -1)
-	//log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
-	if offsets == nil || len(offsets) == 0 || len(offsets) > 1 {
-		return -1, -1, fmt.Errorf("No such id: %s", s)
+	if deleted := idx.trie.Delete(patricia.Prefix(id)); !deleted {
+		return fmt.Errorf("No such id: '%s'", id)
 	}
 	}
-	offsetBefore := offsets[0] + 1
-	offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ")
-	return offsetBefore, offsetAfter, nil
+	return nil
 }
 }
 
 
 func (idx *TruncIndex) Get(s string) (string, error) {
 func (idx *TruncIndex) Get(s string) (string, error) {
 	idx.RLock()
 	idx.RLock()
 	defer idx.RUnlock()
 	defer idx.RUnlock()
-	before, after, err := idx.lookup(s)
-	//log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
-	if err != nil {
-		return "", err
+	var (
+		id string
+	)
+	if s == "" {
+		return "", ErrNoID
+	}
+	subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
+		if id != "" {
+			// we haven't found the ID if there are two or more IDs
+			id = ""
+			return fmt.Errorf("we've found two entries")
+		}
+		id = string(prefix)
+		return nil
+	}
+
+	if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
+		return "", fmt.Errorf("No such id: %s", s)
+	}
+	if id != "" {
+		return id, nil
 	}
 	}
-	return string(idx.bytes[before:after]), err
+	return "", fmt.Errorf("No such id: %s", s)
 }
 }

+ 14 - 1
pkg/truncindex/truncindex_test.go

@@ -26,8 +26,16 @@ func TestTruncIndex(t *testing.T) {
 	if err := index.Add(id); err != nil {
 	if err := index.Add(id); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+
+	// Add an empty id (should fail)
+	if err := index.Add(""); err == nil {
+		t.Fatalf("Adding an empty id should return an error")
+	}
+
 	// Get a non-existing id
 	// Get a non-existing id
 	assertIndexGet(t, index, "abracadabra", "", true)
 	assertIndexGet(t, index, "abracadabra", "", true)
+	// Get an empty id
+	assertIndexGet(t, index, "", "", true)
 	// Get the exact id
 	// Get the exact id
 	assertIndexGet(t, index, id, id, false)
 	assertIndexGet(t, index, id, id, false)
 	// The first letter should match
 	// The first letter should match
@@ -60,6 +68,11 @@ func TestTruncIndex(t *testing.T) {
 		t.Fatalf("Deleting a non-existing id should return an error")
 		t.Fatalf("Deleting a non-existing id should return an error")
 	}
 	}
 
 
+	// Deleting an empty id should return an error
+	if err := index.Delete(""); err == nil {
+		t.Fatal("Deleting an empty id should return an error")
+	}
+
 	// Deleting id2 should remove conflicts
 	// Deleting id2 should remove conflicts
 	if err := index.Delete(id2); err != nil {
 	if err := index.Delete(id2); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -84,7 +97,7 @@ func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult strin
 	if result, err := index.Get(input); err != nil && !expectError {
 	if result, err := index.Get(input); err != nil && !expectError {
 		t.Fatalf("Unexpected error getting '%s': %s", input, err)
 		t.Fatalf("Unexpected error getting '%s': %s", input, err)
 	} else if err == nil && expectError {
 	} else if err == nil && expectError {
-		t.Fatalf("Getting '%s' should return an error", input)
+		t.Fatalf("Getting '%s' should return an error, not '%s'", input, result)
 	} else if result != expectedResult {
 	} else if result != expectedResult {
 		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
 		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
 	}
 	}

+ 25 - 0
vendor/src/github.com/tchap/go-patricia/.gitignore

@@ -0,0 +1,25 @@
+# Swap files.
+*.swp
+
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe

+ 9 - 0
vendor/src/github.com/tchap/go-patricia/.travis.yml

@@ -0,0 +1,9 @@
+language: go
+
+go:
+  - 1.2
+  - tip
+
+branches:
+  exclude:
+    - wip

+ 3 - 0
vendor/src/github.com/tchap/go-patricia/AUTHORS

@@ -0,0 +1,3 @@
+This is the complete list of go-patricia copyright holders:
+
+Ondřej Kupka <ondra.cap@gmail.com>

+ 20 - 0
vendor/src/github.com/tchap/go-patricia/LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 The AUTHORS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 112 - 0
vendor/src/github.com/tchap/go-patricia/README.md

@@ -0,0 +1,112 @@
+# go-patricia #
+
+**Documentation**: [GoDoc](http://godoc.org/github.com/tchap/go-patricia/patricia)<br />
+**Build Status**: [![Build Status](https://travis-ci.org/tchap/go-patricia.png?branch=master)](https://travis-ci.org/tchap/go-patricia)<br >
+**Test Coverage**: Comming as soon as Drone.io people update their Go.
+
+## About ##
+
+A generic patricia trie (also called radix tree) implemented in Go (Golang).
+
+The patricia trie as implemented in this library enables fast visiting of items
+in some particular ways:
+
+1. visit all items saved in the tree,
+2. visit all items matching particular prefix (visit subtree), or
+3. given a string, visit all items matching some prefix of that string.
+
+`[]byte` type is used for keys, `interface{}` for values.
+
+`Trie` is not thread safe. Synchronize the access yourself.
+
+### State of the Project ###
+
+Apparently some people are using this, so the API should not change often.
+Any ideas on how to make the library better are still welcome.
+
+More (unit) testing would be cool as well...
+
+## Usage ##
+
+Import the package from GitHub first.
+
+```go
+import "github.com/tchap/go-patricia/patricia"
+```
+
+Then you can start having fun.
+
+```go
+printItem := func(prefix patricia.Prefix, item patricia.Item) error {
+	fmt.Printf("%q: %v\n", prefix, item)
+	return nil
+}
+
+// Create a new tree.
+trie := NewTrie()
+
+// Insert some items.
+trie.Insert(Prefix("Pepa Novak"), 1)
+trie.Insert(Prefix("Pepa Sindelar"), 2)
+trie.Insert(Prefix("Karel Macha"), 3)
+trie.Insert(Prefix("Karel Hynek Macha"), 4)
+
+// Just check if some things are present in the tree.
+key := Prefix("Pepa Novak")
+fmt.Printf("%q present? %v\n", key, trie.Match(key))
+// "Pepa Novak" present? true
+key = Prefix("Karel")
+fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
+// Anybody called "Karel" here? true
+
+// Walk the tree.
+trie.Visit(printItem)
+// "Pepa Novak": 1
+// "Pepa Sindelar": 2
+// "Karel Macha": 3
+// "Karel Hynek Macha": 4
+
+// Walk a subtree.
+trie.VisitSubtree(Prefix("Pepa"), printItem)
+// "Pepa Novak": 1
+// "Pepa Sindelar": 2
+
+// Modify an item, then fetch it from the tree.
+trie.Set(Prefix("Karel Hynek Macha"), 10)
+key = Prefix("Karel Hynek Macha")
+fmt.Printf("%q: %v\n", key, trie.Get(key))
+// "Karel Hynek Macha": 10
+
+// Walk prefixes.
+prefix := Prefix("Karel Hynek Macha je kouzelnik")
+trie.VisitPrefixes(prefix, printItem)
+// "Karel Hynek Macha": 10
+
+// Delete some items.
+trie.Delete(Prefix("Pepa Novak"))
+trie.Delete(Prefix("Karel Macha"))
+
+// Walk again.
+trie.Visit(printItem)
+// "Pepa Sindelar": 2
+// "Karel Hynek Macha": 10
+
+// Delete a subtree.
+trie.DeleteSubtree(Prefix("Pepa"))
+
+// Print what is left.
+trie.Visit(printItem)
+// "Karel Hynek Macha": 10
+```
+
+## License ##
+
+MIT, check the `LICENSE` file.
+
+[![Gittip
+Badge](http://img.shields.io/gittip/alanhamlett.png)](https://www.gittip.com/tchap/
+"Gittip Badge")
+
+[![Bitdeli
+Badge](https://d2weczhvl823v0.cloudfront.net/tchap/go-patricia/trend.png)](https://bitdeli.com/free
+"Bitdeli Badge")

+ 231 - 0
vendor/src/github.com/tchap/go-patricia/patricia/children.go

@@ -0,0 +1,231 @@
+// Copyright (c) 2014 The go-patricia AUTHORS
+//
+// Use of this source code is governed by The MIT License
+// that can be found in the LICENSE file.
+
+package patricia
+
+const (
+	// Max prefix length that is kept in a single trie node.
+	MaxPrefixPerNode = 10
+	// Max children to keep in a node in the sparse mode.
+	MaxChildrenPerSparseNode = 8
+)
+
+type childList interface {
+	length() int
+	head() *Trie
+	add(child *Trie) childList
+	replace(b byte, child *Trie)
+	remove(child *Trie)
+	next(b byte) *Trie
+	walk(prefix *Prefix, visitor VisitorFunc) error
+}
+
+type sparseChildList struct {
+	children []*Trie
+}
+
+func newSparseChildList() childList {
+	return &sparseChildList{
+		children: make([]*Trie, 0, MaxChildrenPerSparseNode),
+	}
+}
+
+func (list *sparseChildList) length() int {
+	return len(list.children)
+}
+
+func (list *sparseChildList) head() *Trie {
+	return list.children[0]
+}
+
+func (list *sparseChildList) add(child *Trie) childList {
+	// Search for an empty spot and insert the child if possible.
+	if len(list.children) != cap(list.children) {
+		list.children = append(list.children, child)
+		return list
+	}
+
+	// Otherwise we have to transform to the dense list type.
+	return newDenseChildList(list, child)
+}
+
+func (list *sparseChildList) replace(b byte, child *Trie) {
+	// Seek the child and replace it.
+	for i, node := range list.children {
+		if node.prefix[0] == b {
+			list.children[i] = child
+			return
+		}
+	}
+}
+
+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 {
+			return child
+		}
+	}
+	return nil
+}
+
+func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
+	for _, child := range list.children {
+		*prefix = append(*prefix, child.prefix...)
+		if child.item != nil {
+			err := visitor(*prefix, child.item)
+			if err != nil {
+				if err == SkipSubtree {
+					*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
+					continue
+				}
+				*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
+				return err
+			}
+		}
+
+		err := child.children.walk(prefix, visitor)
+		*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+type denseChildList struct {
+	min      int
+	max      int
+	children []*Trie
+}
+
+func newDenseChildList(list *sparseChildList, child *Trie) childList {
+	var (
+		min int = 255
+		max int = 0
+	)
+	for _, child := range list.children {
+		b := int(child.prefix[0])
+		if b < min {
+			min = b
+		}
+		if b > max {
+			max = b
+		}
+	}
+
+	b := int(child.prefix[0])
+	if b < min {
+		min = b
+	}
+	if b > max {
+		max = b
+	}
+
+	children := make([]*Trie, max-min+1)
+	for _, child := range list.children {
+		children[int(child.prefix[0])-min] = child
+	}
+	children[int(child.prefix[0])-min] = child
+
+	return &denseChildList{min, max, children}
+}
+
+func (list *denseChildList) length() int {
+	return list.max - list.min + 1
+}
+
+func (list *denseChildList) head() *Trie {
+	return list.children[0]
+}
+
+func (list *denseChildList) add(child *Trie) childList {
+	b := int(child.prefix[0])
+
+	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
+
+	case b < list.min:
+		children := make([]*Trie, list.max-b+1)
+		children[0] = 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
+		copy(children, list.children)
+		list.children = children
+		list.max = b
+	}
+
+	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
+	if list.children[i] == nil {
+		// This is not supposed to be reached.
+		panic("removing non-existent child")
+	}
+	list.children[i] = nil
+}
+
+func (list *denseChildList) next(b byte) *Trie {
+	i := int(b)
+	if i < list.min || list.max < i {
+		return nil
+	}
+	return list.children[i-list.min]
+}
+
+func (list *denseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
+	for _, child := range list.children {
+		if child == nil {
+			continue
+		}
+		*prefix = append(*prefix, child.prefix...)
+		if child.item != nil {
+			if err := visitor(*prefix, child.item); err != nil {
+				if err == SkipSubtree {
+					*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
+					continue
+				}
+				*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
+				return err
+			}
+		}
+
+		err := child.children.walk(prefix, visitor)
+		*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 432 - 0
vendor/src/github.com/tchap/go-patricia/patricia/patricia.go

@@ -0,0 +1,432 @@
+// Copyright (c) 2014 The go-patricia AUTHORS
+//
+// Use of this source code is governed by The MIT License
+// that can be found in the LICENSE file.
+
+package patricia
+
+import (
+	"errors"
+)
+
+//------------------------------------------------------------------------------
+// Trie
+//------------------------------------------------------------------------------
+
+type (
+	Prefix      []byte
+	Item        interface{}
+	VisitorFunc func(prefix Prefix, item Item) error
+)
+
+// Trie is a generic patricia trie that allows fast retrieval of items by prefix.
+// and other funky stuff.
+//
+// Trie is not thread-safe.
+type Trie struct {
+	prefix Prefix
+	item   Item
+
+	children childList
+}
+
+// Public API ------------------------------------------------------------------
+
+// Trie constructor.
+func NewTrie() *Trie {
+	return &Trie{
+		children: newSparseChildList(),
+	}
+}
+
+// Item returns the item stored in the root of this trie.
+func (trie *Trie) Item() Item {
+	return trie.item
+}
+
+// Insert inserts a new item into the trie using the given prefix. Insert does
+// not replace existing items. It returns false if an item was already in place.
+func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) {
+	return trie.put(key, item, false)
+}
+
+// Set works much like Insert, but it always sets the item, possibly replacing
+// the item previously inserted.
+func (trie *Trie) Set(key Prefix, item Item) {
+	trie.put(key, item, true)
+}
+
+// Get returns the item located at key.
+//
+// This method is a bit dangerous, because Get can as well end up in an internal
+// node that is not really representing any user-defined value. So when nil is
+// a valid value being used, it is not possible to tell if the value was inserted
+// into the tree by the user or not. A possible workaround for this is not to use
+// nil interface as a valid value, even using zero value of any type is enough
+// to prevent this bad behaviour.
+func (trie *Trie) Get(key Prefix) (item Item) {
+	_, node, found, leftover := trie.findSubtree(key)
+	if !found || len(leftover) != 0 {
+		return nil
+	}
+	return node.item
+}
+
+// Match returns what Get(prefix) != nil would return. The same warning as for
+// Get applies here as well.
+func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) {
+	return trie.Get(prefix) != nil
+}
+
+// MatchSubtree returns true when there is a subtree representing extensions
+// to key, that is if there are any keys in the tree which have key as prefix.
+func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
+	_, _, matched, _ = trie.findSubtree(key)
+	return
+}
+
+// Visit calls visitor on every node containing a non-nil item.
+//
+// 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
+// case Visit skips the subtree represented by the current node and continues
+// elsewhere.
+func (trie *Trie) Visit(visitor VisitorFunc) error {
+	return trie.walk(nil, visitor)
+}
+
+// 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.
+	if prefix == nil {
+		panic(ErrNilPrefix)
+	}
+
+	// Empty trie must be handled explicitly.
+	if trie.prefix == nil {
+		return nil
+	}
+
+	// Locate the relevant subtree.
+	_, root, found, leftover := trie.findSubtree(prefix)
+	if !found {
+		return nil
+	}
+	prefix = append(prefix, leftover...)
+
+	// Visit it.
+	return root.walk(prefix, visitor)
+}
+
+// VisitPrefixes visits only nodes that represent prefixes of key.
+// To say the obvious, returning SkipSubtree from visitor makes no sense here.
+func (trie *Trie) VisitPrefixes(key Prefix, visitor VisitorFunc) error {
+	// Nil key not allowed.
+	if key == nil {
+		panic(ErrNilPrefix)
+	}
+
+	// Empty trie must be handled explicitly.
+	if trie.prefix == nil {
+		return nil
+	}
+
+	// Walk the path matching key prefixes.
+	node := trie
+	prefix := key
+	offset := 0
+	for {
+		// Compute what part of prefix matches.
+		common := node.longestCommonPrefixLength(key)
+		key = key[common:]
+		offset += common
+
+		// Partial match means that there is no subtree matching prefix.
+		if common < len(node.prefix) {
+			return nil
+		}
+
+		// Call the visitor.
+		if item := node.item; item != nil {
+			if err := visitor(prefix[:offset], item); err != nil {
+				return err
+			}
+		}
+
+		if len(key) == 0 {
+			// This node represents key, we are finished.
+			return nil
+		}
+
+		// There is some key suffix left, move to the children.
+		child := node.children.next(key[0])
+		if child == nil {
+			// There is nowhere to continue, return.
+			return nil
+		}
+
+		node = child
+	}
+}
+
+// Delete deletes the item represented by the given prefix.
+//
+// True is returned if the matching node was found and deleted.
+func (trie *Trie) Delete(key Prefix) (deleted bool) {
+	// Nil prefix not allowed.
+	if key == nil {
+		panic(ErrNilPrefix)
+	}
+
+	// Empty trie must be handled explicitly.
+	if trie.prefix == nil {
+		return false
+	}
+
+	// Find the relevant node.
+	parent, node, _, leftover := trie.findSubtree(key)
+	if len(leftover) != 0 {
+		return false
+	}
+
+	// If the item is already set to nil, there is nothing to do.
+	if node.item == nil {
+		return false
+	}
+
+	// Delete the item.
+	node.item = nil
+
+	// Compact since that might be possible now.
+	if compacted := node.compact(); compacted != node {
+		if parent == nil {
+			*node = *compacted
+		} else {
+			parent.children.replace(node.prefix[0], compacted)
+			*parent = *parent.compact()
+		}
+	}
+
+	return true
+}
+
+// DeleteSubtree finds the subtree exactly matching prefix and deletes it.
+//
+// True is returned if the subtree was found and deleted.
+func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
+	// Nil prefix not allowed.
+	if prefix == nil {
+		panic(ErrNilPrefix)
+	}
+
+	// Empty trie must be handled explicitly.
+	if trie.prefix == nil {
+		return false
+	}
+
+	// Locate the relevant subtree.
+	parent, root, found, _ := trie.findSubtree(prefix)
+	if !found {
+		return false
+	}
+
+	// If we are in the root of the trie, reset the trie.
+	if parent == nil {
+		root.prefix = nil
+		root.children = newSparseChildList()
+		return true
+	}
+
+	// Otherwise remove the root node from its parent.
+	parent.children.remove(root)
+	return true
+}
+
+// Internal helper methods -----------------------------------------------------
+
+func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
+	// Nil prefix not allowed.
+	if key == nil {
+		panic(ErrNilPrefix)
+	}
+
+	var (
+		common int
+		node   *Trie = trie
+		child  *Trie
+	)
+
+	if node.prefix == nil {
+		if len(key) <= MaxPrefixPerNode {
+			node.prefix = key
+			goto InsertItem
+		}
+		node.prefix = key[:MaxPrefixPerNode]
+		key = key[MaxPrefixPerNode:]
+		goto AppendChild
+	}
+
+	for {
+		// Compute the longest common prefix length.
+		common = node.longestCommonPrefixLength(key)
+		key = key[common:]
+
+		// Only a part matches, split.
+		if common < len(node.prefix) {
+			goto SplitPrefix
+		}
+
+		// common == len(node.prefix) since never (common > len(node.prefix))
+		// common == len(former key) <-> 0 == len(key)
+		// -> former key == node.prefix
+		if len(key) == 0 {
+			goto InsertItem
+		}
+
+		// Check children for matching prefix.
+		child = node.children.next(key[0])
+		if child == nil {
+			goto AppendChild
+		}
+		node = child
+	}
+
+SplitPrefix:
+	// Split the prefix if necessary.
+	child = new(Trie)
+	*child = *node
+	*node = *NewTrie()
+	node.prefix = child.prefix[:common]
+	child.prefix = child.prefix[common:]
+	child = child.compact()
+	node.children = node.children.add(child)
+
+AppendChild:
+	// Keep appending children until whole prefix is inserted.
+	// This loop starts with empty node.prefix that needs to be filled.
+	for len(key) != 0 {
+		child := NewTrie()
+		if len(key) <= MaxPrefixPerNode {
+			child.prefix = key
+			node.children = node.children.add(child)
+			node = child
+			goto InsertItem
+		} else {
+			child.prefix = key[:MaxPrefixPerNode]
+			key = key[MaxPrefixPerNode:]
+			node.children = node.children.add(child)
+			node = child
+		}
+	}
+
+InsertItem:
+	// Try to insert the item if possible.
+	if replace || node.item == nil {
+		node.item = item
+		return true
+	}
+	return false
+}
+
+func (trie *Trie) compact() *Trie {
+	// Only a node with a single child can be compacted.
+	if trie.children.length() != 1 {
+		return trie
+	}
+
+	child := trie.children.head()
+
+	// If any item is set, we cannot compact since we want to retain
+	// the ability to do searching by key. This makes compaction less usable,
+	// but that simply cannot be avoided.
+	if trie.item != nil || child.item != nil {
+		return trie
+	}
+
+	// Make sure the combined prefixes fit into a single node.
+	if len(trie.prefix)+len(child.prefix) > MaxPrefixPerNode {
+		return trie
+	}
+
+	// Concatenate the prefixes, move the items.
+	child.prefix = append(trie.prefix, child.prefix...)
+	if trie.item != nil {
+		child.item = trie.item
+	}
+
+	return child
+}
+
+func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) {
+	// Find the subtree matching prefix.
+	root = trie
+	for {
+		// 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 {
+			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
+		}
+
+		parent = root
+		root = child
+	}
+}
+
+func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error {
+	var prefix Prefix
+	// Allocate a bit more space for prefix at the beginning.
+	if actualRootPrefix == nil {
+		prefix = make(Prefix, 32+len(trie.prefix))
+		copy(prefix, trie.prefix)
+		prefix = prefix[:len(trie.prefix)]
+	} else {
+		prefix = make(Prefix, 32+len(actualRootPrefix))
+		copy(prefix, actualRootPrefix)
+		prefix = prefix[:len(actualRootPrefix)]
+	}
+
+	// Visit the root first. Not that this works for empty trie as well since
+	// in that case item == nil && len(children) == 0.
+	if trie.item != nil {
+		if err := visitor(prefix, trie.item); err != nil {
+			if err == SkipSubtree {
+				return nil
+			}
+			return err
+		}
+	}
+
+	// Then continue to the children.
+	return trie.children.walk(&prefix, visitor)
+}
+
+func (trie *Trie) longestCommonPrefixLength(prefix Prefix) (i int) {
+	for ; i < len(prefix) && i < len(trie.prefix) && prefix[i] == trie.prefix[i]; i++ {
+	}
+	return
+}
+
+// Errors ----------------------------------------------------------------------
+
+var (
+	SkipSubtree  = errors.New("Skip this subtree")
+	ErrNilPrefix = errors.New("Nil prefix passed into a method call")
+)

+ 161 - 0
vendor/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go

@@ -0,0 +1,161 @@
+// Copyright (c) 2014 The go-patricia AUTHORS
+//
+// Use of this source code is governed by The MIT License
+// that can be found in the LICENSE file.
+
+package patricia
+
+import (
+	"testing"
+)
+
+// Tests -----------------------------------------------------------------------
+
+func TestTrie_InsertDense(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"aba", 0, success},
+		{"abb", 1, success},
+		{"abc", 2, success},
+		{"abd", 3, success},
+		{"abe", 4, success},
+		{"abf", 5, success},
+		{"abg", 6, success},
+		{"abh", 7, success},
+		{"abi", 8, success},
+		{"abj", 9, success},
+		{"abk", 0, success},
+		{"abl", 1, success},
+		{"abm", 2, success},
+		{"abn", 3, success},
+		{"abo", 4, success},
+		{"abp", 5, success},
+		{"abq", 6, success},
+		{"abr", 7, success},
+		{"abs", 8, success},
+		{"abt", 9, success},
+		{"abu", 0, success},
+		{"abv", 1, success},
+		{"abw", 2, success},
+		{"abx", 3, success},
+		{"aby", 4, success},
+		{"abz", 5, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+func TestTrie_InsertDensePreceeding(t *testing.T) {
+	trie := NewTrie()
+	start := byte(70)
+	// create a dense node
+	for i := byte(0); i <= MaxChildrenPerSparseNode; i++ {
+		if !trie.Insert(Prefix([]byte{start + i}), true) {
+			t.Errorf("insert failed, prefix=%v", start+i)
+		}
+	}
+	// insert some preceeding keys
+	for i := byte(1); i < start; i *= i + 1 {
+		if !trie.Insert(Prefix([]byte{start - i}), true) {
+			t.Errorf("insert failed, prefix=%v", start-i)
+		}
+	}
+}
+
+func TestTrie_InsertDenseDuplicatePrefixes(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"aba", 0, success},
+		{"abb", 1, success},
+		{"abc", 2, success},
+		{"abd", 3, success},
+		{"abe", 4, success},
+		{"abf", 5, success},
+		{"abg", 6, success},
+		{"abh", 7, success},
+		{"abi", 8, success},
+		{"abj", 9, success},
+		{"abk", 0, success},
+		{"abl", 1, success},
+		{"abm", 2, success},
+		{"abn", 3, success},
+		{"abo", 4, success},
+		{"abp", 5, success},
+		{"abq", 6, success},
+		{"abr", 7, success},
+		{"abs", 8, success},
+		{"abt", 9, success},
+		{"abu", 0, success},
+		{"abv", 1, success},
+		{"abw", 2, success},
+		{"abx", 3, success},
+		{"aby", 4, success},
+		{"abz", 5, success},
+		{"aba", 0, failure},
+		{"abb", 1, failure},
+		{"abc", 2, failure},
+		{"abd", 3, failure},
+		{"abe", 4, failure},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+func TestTrie_DeleteDense(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"aba", 0, success},
+		{"abb", 1, success},
+		{"abc", 2, success},
+		{"abd", 3, success},
+		{"abe", 4, success},
+		{"abf", 5, success},
+		{"abg", 6, success},
+		{"abh", 7, success},
+		{"abi", 8, success},
+		{"abj", 9, success},
+		{"abk", 0, success},
+		{"abl", 1, success},
+		{"abm", 2, success},
+		{"abn", 3, success},
+		{"abo", 4, success},
+		{"abp", 5, success},
+		{"abq", 6, success},
+		{"abr", 7, success},
+		{"abs", 8, success},
+		{"abt", 9, success},
+		{"abu", 0, success},
+		{"abv", 1, success},
+		{"abw", 2, success},
+		{"abx", 3, success},
+		{"aby", 4, success},
+		{"abz", 5, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	for _, v := range data {
+		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
+		if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}

+ 659 - 0
vendor/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go

@@ -0,0 +1,659 @@
+// Copyright (c) 2014 The go-patricia AUTHORS
+//
+// Use of this source code is governed by The MIT License
+// that can be found in the LICENSE file.
+
+package patricia
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"strings"
+	"testing"
+)
+
+const (
+	success = true
+	failure = false
+)
+
+type testData struct {
+	key    string
+	value  interface{}
+	retVal bool
+}
+
+// Tests -----------------------------------------------------------------------
+
+func TestTrie_InsertDifferentPrefixes(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success},
+		{"Honzooooooooooooooo", "Honza Novak", success},
+		{"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+func TestTrie_InsertDuplicatePrefixes(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Pepan", "Pepan Zdepan", failure},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+func TestTrie_InsertVariousPrefixes(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Pepin", "Pepin Omacka", success},
+		{"Honza", "Honza Novak", success},
+		{"Jenik", "Jenik Poustevnicek", success},
+		{"Pepan", "Pepan Dupan", failure},
+		{"Karel", "Karel Pekar", success},
+		{"Jenik", "Jenik Poustevnicek", failure},
+		{"Pepanek", "Pepanek Zemlicka", success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+func TestTrie_InsertAndMatchPrefix(t *testing.T) {
+	trie := NewTrie()
+	t.Log("INSERT prefix=by week")
+	trie.Insert(Prefix("by week"), 2)
+	t.Log("INSERT prefix=by")
+	trie.Insert(Prefix("by"), 1)
+
+	if !trie.Match(Prefix("by")) {
+		t.Error("MATCH prefix=by, expected=true, got=false")
+	}
+}
+
+func TestTrie_SetGet(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Pepin", "Pepin Omacka", success},
+		{"Honza", "Honza Novak", success},
+		{"Jenik", "Jenik Poustevnicek", success},
+		{"Pepan", "Pepan Dupan", failure},
+		{"Karel", "Karel Pekar", success},
+		{"Jenik", "Jenik Poustevnicek", failure},
+		{"Pepanek", "Pepanek Zemlicka", success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	for _, v := range data {
+		t.Logf("SET %q to 10", v.key)
+		trie.Set(Prefix(v.key), 10)
+	}
+
+	for _, v := range data {
+		value := trie.Get(Prefix(v.key))
+		t.Logf("GET %q => %v", v.key, value)
+		if value.(int) != 10 {
+			t.Errorf("Unexpected return value, != 10", value)
+		}
+	}
+
+	if value := trie.Get(Prefix("random crap")); value != nil {
+		t.Errorf("Unexpected return value, %v != <nil>", value)
+	}
+}
+
+func TestTrie_Match(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Pepin", "Pepin Omacka", success},
+		{"Honza", "Honza Novak", success},
+		{"Jenik", "Jenik Poustevnicek", success},
+		{"Pepan", "Pepan Dupan", failure},
+		{"Karel", "Karel Pekar", success},
+		{"Jenik", "Jenik Poustevnicek", failure},
+		{"Pepanek", "Pepanek Zemlicka", success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	for _, v := range data {
+		matched := trie.Match(Prefix(v.key))
+		t.Logf("MATCH %q => %v", v.key, matched)
+		if !matched {
+			t.Errorf("Inserted key %q was not matched", v.key)
+		}
+	}
+
+	if trie.Match(Prefix("random crap")) {
+		t.Errorf("Key that was not inserted matched: %q", "random crap")
+	}
+}
+
+func TestTrie_MatchFalsePositive(t *testing.T) {
+	trie := NewTrie()
+
+	if ok := trie.Insert(Prefix("A"), 1); !ok {
+		t.Fatal("INSERT prefix=A, item=1 not ok")
+	}
+
+	resultMatchSubtree := trie.MatchSubtree(Prefix("A extra"))
+	resultMatch := trie.Match(Prefix("A extra"))
+
+	if resultMatchSubtree != false {
+		t.Error("MatchSubtree returned false positive")
+	}
+
+	if resultMatch != false {
+		t.Error("Match returned false positive")
+	}
+}
+
+func TestTrie_MatchSubtree(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Pepin", "Pepin Omacka", success},
+		{"Honza", "Honza Novak", success},
+		{"Jenik", "Jenik Poustevnicek", success},
+		{"Pepan", "Pepan Dupan", failure},
+		{"Karel", "Karel Pekar", success},
+		{"Jenik", "Jenik Poustevnicek", failure},
+		{"Pepanek", "Pepanek Zemlicka", success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	for _, v := range data {
+		key := Prefix(v.key[:3])
+		matched := trie.MatchSubtree(key)
+		t.Logf("MATCH_SUBTREE %q => %v", key, matched)
+		if !matched {
+			t.Errorf("Subtree %q was not matched", v.key)
+		}
+	}
+}
+
+func TestTrie_Visit(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepa", 0, success},
+		{"Pepa Zdepa", 1, success},
+		{"Pepa Kuchar", 2, success},
+		{"Honza", 3, success},
+		{"Jenik", 4, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	if err := trie.Visit(func(prefix Prefix, item Item) error {
+		name := data[item.(int)].key
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
+		if !strings.HasPrefix(string(prefix), name) {
+			t.Errorf("Unexpected prefix encountered, %q not a prefix of %q", prefix, name)
+		}
+		return nil
+	}); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestTrie_VisitSkipSubtree(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepa", 0, success},
+		{"Pepa Zdepa", 1, success},
+		{"Pepa Kuchar", 2, success},
+		{"Honza", 3, success},
+		{"Jenik", 4, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	if err := trie.Visit(func(prefix Prefix, item Item) error {
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
+		if item.(int) == 0 {
+			t.Logf("SKIP %q", prefix)
+			return SkipSubtree
+		}
+		if strings.HasPrefix(string(prefix), "Pepa") {
+			t.Errorf("Unexpected prefix encountered, %q", prefix)
+		}
+		return nil
+	}); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestTrie_VisitReturnError(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepa", 0, success},
+		{"Pepa Zdepa", 1, success},
+		{"Pepa Kuchar", 2, success},
+		{"Honza", 3, success},
+		{"Jenik", 4, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	someErr := errors.New("Something exploded")
+	if err := trie.Visit(func(prefix Prefix, item Item) error {
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
+		if item.(int) == 0 {
+			return someErr
+		}
+		if item.(int) != 0 {
+			t.Errorf("Unexpected prefix encountered, %q", prefix)
+		}
+		return nil
+	}); err != nil && err != someErr {
+		t.Fatal(err)
+	}
+}
+
+func TestTrie_VisitSubtree(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepa", 0, success},
+		{"Pepa Zdepa", 1, success},
+		{"Pepa Kuchar", 2, success},
+		{"Honza", 3, success},
+		{"Jenik", 4, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	var counter int
+	subtreePrefix := []byte("Pep")
+	t.Log("VISIT Pep")
+	if err := trie.VisitSubtree(subtreePrefix, func(prefix Prefix, item Item) error {
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
+		if !bytes.HasPrefix(prefix, subtreePrefix) {
+			t.Errorf("Unexpected prefix encountered, %q does not extend %q",
+				prefix, subtreePrefix)
+		}
+		if len(prefix) > len(data[item.(int)].key) {
+			t.Fatalf("Something is rather fishy here, prefix=%q", prefix)
+		}
+		counter++
+		return nil
+	}); err != nil {
+		t.Fatal(err)
+	}
+
+	if counter != 3 {
+		t.Error("Unexpected number of nodes visited")
+	}
+}
+
+func TestTrie_VisitPrefixes(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"P", 0, success},
+		{"Pe", 1, success},
+		{"Pep", 2, success},
+		{"Pepa", 3, success},
+		{"Pepa Zdepa", 4, success},
+		{"Pepa Kuchar", 5, success},
+		{"Honza", 6, success},
+		{"Jenik", 7, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	var counter int
+	word := []byte("Pepa")
+	if err := trie.VisitPrefixes(word, func(prefix Prefix, item Item) error {
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
+		if !bytes.HasPrefix(word, prefix) {
+			t.Errorf("Unexpected prefix encountered, %q is not a prefix of %q",
+				prefix, word)
+		}
+		counter++
+		return nil
+	}); err != nil {
+		t.Fatal(err)
+	}
+
+	if counter != 4 {
+		t.Error("Unexpected number of nodes visited")
+	}
+}
+
+func TestParticiaTrie_Delete(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Honza", "Honza Novak", success},
+		{"Jenik", "Jenik Poustevnicek", success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	for _, v := range data {
+		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
+		if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+func TestParticiaTrie_DeleteNonExistent(t *testing.T) {
+	trie := NewTrie()
+
+	insertData := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Honza", "Honza Novak", success},
+		{"Jenik", "Jenik Poustevnicek", success},
+	}
+	deleteData := []testData{
+		{"Pepan", "Pepan Zdepan", success},
+		{"Honza", "Honza Novak", success},
+		{"Pepan", "Pepan Zdepan", failure},
+		{"Jenik", "Jenik Poustevnicek", success},
+		{"Honza", "Honza Novak", failure},
+	}
+
+	for _, v := range insertData {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	for _, v := range deleteData {
+		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
+		if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+func TestParticiaTrie_DeleteSubtree(t *testing.T) {
+	trie := NewTrie()
+
+	insertData := []testData{
+		{"P", 0, success},
+		{"Pe", 1, success},
+		{"Pep", 2, success},
+		{"Pepa", 3, success},
+		{"Pepa Zdepa", 4, success},
+		{"Pepa Kuchar", 5, success},
+		{"Honza", 6, success},
+		{"Jenik", 7, success},
+	}
+	deleteData := []testData{
+		{"Pe", -1, success},
+		{"Pe", -1, failure},
+		{"Honzik", -1, failure},
+		{"Honza", -1, success},
+		{"Honza", -1, failure},
+		{"Pep", -1, failure},
+		{"P", -1, success},
+		{"Nobody", -1, failure},
+		{"", -1, success},
+	}
+
+	for _, v := range insertData {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	for _, v := range deleteData {
+		t.Logf("DELETE_SUBTREE prefix=%v, success=%v", v.key, v.retVal)
+		if ok := trie.DeleteSubtree([]byte(v.key)); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+}
+
+/*
+func TestTrie_Dump(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"Honda", nil, success},
+		{"Honza", nil, success},
+		{"Jenik", nil, success},
+		{"Pepan", nil, success},
+		{"Pepin", nil, success},
+	}
+
+	for i, v := range data {
+		if _, ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
+			t.Logf("INSERT %v %v", v.key, v.value)
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", i, ok)
+		}
+	}
+
+	dump := `
++--+--+ Hon +--+--+ da
+   |           |
+   |           +--+ za
+   |
+   +--+ Jenik
+   |
+   +--+ Pep +--+--+ an
+               |
+               +--+ in
+`
+
+	var buf bytes.Buffer
+	trie.Dump(buf)
+
+	if !bytes.Equal(buf.Bytes(), dump) {
+		t.Logf("DUMP")
+		t.Fatalf("Unexpected dump generated, expected\n\n%v\ngot\n\n%v", dump, buf.String())
+	}
+}
+*/
+
+func TestTrie_compact(t *testing.T) {
+	trie := NewTrie()
+
+	trie.Insert(Prefix("a"), 0)
+	trie.Insert(Prefix("ab"), 0)
+	trie.Insert(Prefix("abc"), 0)
+	trie.Insert(Prefix("abcd"), 0)
+	trie.Insert(Prefix("abcde"), 0)
+	trie.Insert(Prefix("abcdef"), 0)
+	trie.Insert(Prefix("abcdefg"), 0)
+	trie.Insert(Prefix("abcdefgi"), 0)
+	trie.Insert(Prefix("abcdefgij"), 0)
+	trie.Insert(Prefix("abcdefgijk"), 0)
+
+	trie.Delete(Prefix("abcdef"))
+	trie.Delete(Prefix("abcde"))
+	trie.Delete(Prefix("abcdefg"))
+
+	trie.Delete(Prefix("a"))
+	trie.Delete(Prefix("abc"))
+	trie.Delete(Prefix("ab"))
+
+	trie.Visit(func(prefix Prefix, item Item) error {
+		// 97 ~~ 'a',
+		for ch := byte(97); ch <= 107; ch++ {
+			if c := bytes.Count(prefix, []byte{ch}); c > 1 {
+				t.Errorf("%q appeared in %q %v times", ch, prefix, c)
+			}
+		}
+		return nil
+	})
+}
+
+func TestTrie_longestCommonPrefixLenght(t *testing.T) {
+	trie := NewTrie()
+	trie.prefix = []byte("1234567890")
+
+	switch {
+	case trie.longestCommonPrefixLength([]byte("")) != 0:
+		t.Fail()
+	case trie.longestCommonPrefixLength([]byte("12345")) != 5:
+		t.Fail()
+	case trie.longestCommonPrefixLength([]byte("123789")) != 3:
+		t.Fail()
+	case trie.longestCommonPrefixLength([]byte("12345678901")) != 10:
+		t.Fail()
+	}
+}
+
+// Examples --------------------------------------------------------------------
+
+func ExampleTrie() {
+	// Create a new tree.
+	trie := NewTrie()
+
+	// Insert some items.
+	trie.Insert(Prefix("Pepa Novak"), 1)
+	trie.Insert(Prefix("Pepa Sindelar"), 2)
+	trie.Insert(Prefix("Karel Macha"), 3)
+	trie.Insert(Prefix("Karel Hynek Macha"), 4)
+
+	// Just check if some things are present in the tree.
+	key := Prefix("Pepa Novak")
+	fmt.Printf("%q present? %v\n", key, trie.Match(key))
+	key = Prefix("Karel")
+	fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
+
+	// Walk the tree.
+	trie.Visit(printItem)
+	// "Pepa Novak": 1
+	// "Pepa Sindelar": 2
+	// "Karel Macha": 3
+	// "Karel Hynek Macha": 4
+
+	// Walk a subtree.
+	trie.VisitSubtree(Prefix("Pepa"), printItem)
+	// "Pepa Novak": 1
+	// "Pepa Sindelar": 2
+
+	// Modify an item, then fetch it from the tree.
+	trie.Set(Prefix("Karel Hynek Macha"), 10)
+	key = Prefix("Karel Hynek Macha")
+	fmt.Printf("%q: %v\n", key, trie.Get(key))
+	// "Karel Hynek Macha": 10
+
+	// Walk prefixes.
+	prefix := Prefix("Karel Hynek Macha je kouzelnik")
+	trie.VisitPrefixes(prefix, printItem)
+	// "Karel Hynek Macha": 10
+
+	// Delete some items.
+	trie.Delete(Prefix("Pepa Novak"))
+	trie.Delete(Prefix("Karel Macha"))
+
+	// Walk again.
+	trie.Visit(printItem)
+	// "Pepa Sindelar": 2
+	// "Karel Hynek Macha": 10
+
+	// Delete a subtree.
+	trie.DeleteSubtree(Prefix("Pepa"))
+
+	// Print what is left.
+	trie.Visit(printItem)
+	// "Karel Hynek Macha": 10
+
+	// Output:
+	// "Pepa Novak" present? true
+	// Anybody called "Karel" here? true
+	// "Pepa Novak": 1
+	// "Pepa Sindelar": 2
+	// "Karel Macha": 3
+	// "Karel Hynek Macha": 4
+	// "Pepa Novak": 1
+	// "Pepa Sindelar": 2
+	// "Karel Hynek Macha": 10
+	// "Karel Hynek Macha": 10
+	// "Pepa Sindelar": 2
+	// "Karel Hynek Macha": 10
+	// "Karel Hynek Macha": 10
+}
+
+// Helpers ---------------------------------------------------------------------
+
+func printItem(prefix Prefix, item Item) error {
+	fmt.Printf("%q: %v\n", prefix, item)
+	return nil
+}

+ 78 - 0
vendor/src/github.com/tchap/go-patricia/patricia/patricia_test.go

@@ -0,0 +1,78 @@
+// Copyright (c) 2014 The go-patricia AUTHORS
+//
+// Use of this source code is governed by The MIT License
+// that can be found in the LICENSE file.
+
+package patricia
+
+import (
+	"crypto/rand"
+	"reflect"
+	"testing"
+)
+
+// Tests -----------------------------------------------------------------------
+
+func TestTrie_GetNonexistentPrefix(t *testing.T) {
+	trie := NewTrie()
+
+	data := []testData{
+		{"aba", 0, success},
+	}
+
+	for _, v := range data {
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
+		}
+	}
+
+	t.Logf("GET prefix=baa, expect item=nil")
+	if item := trie.Get(Prefix("baa")); item != nil {
+		t.Errorf("Unexpected return value, expected=<nil>, got=%v", item)
+	}
+}
+
+func TestTrie_RandomKitchenSink(t *testing.T) {
+	if testing.Short() {
+		t.Skip()
+	}
+	const count, size = 750000, 16
+	b := make([]byte, count+size+1)
+	if _, err := rand.Read(b); err != nil {
+		t.Fatal("error generating random bytes", err)
+	}
+	m := make(map[string]string)
+	for i := 0; i < count; i++ {
+		m[string(b[i:i+size])] = string(b[i+1 : i+size+1])
+	}
+	trie := NewTrie()
+	getAndDelete := func(k, v string) {
+		i := trie.Get(Prefix(k))
+		if i == nil {
+			t.Fatalf("item not found, prefix=%v", []byte(k))
+		} else if s, ok := i.(string); !ok {
+			t.Fatalf("unexpected item type, expecting=%v, got=%v", reflect.TypeOf(k), reflect.TypeOf(i))
+		} else if s != v {
+			t.Fatalf("unexpected item, expecting=%v, got=%v", []byte(k), []byte(s))
+		} else if !trie.Delete(Prefix(k)) {
+			t.Fatalf("delete failed, prefix=%v", []byte(k))
+		} else if i = trie.Get(Prefix(k)); i != nil {
+			t.Fatalf("unexpected item, expecting=<nil>, got=%v", i)
+		} else if trie.Delete(Prefix(k)) {
+			t.Fatalf("extra delete succeeded, prefix=%v", []byte(k))
+		}
+	}
+	for k, v := range m {
+		if !trie.Insert(Prefix(k), v) {
+			t.Fatalf("insert failed, prefix=%v", []byte(k))
+		}
+		if byte(k[size/2]) < 128 {
+			getAndDelete(k, v)
+			delete(m, k)
+		}
+	}
+	for k, v := range m {
+		getAndDelete(k, v)
+	}
+}