VMware vSphere Integrated Containers provider (#206)
* Add Virtual Kubelet provider for VIC Initial virtual kubelet provider for VMware VIC. This provider currently handles creating and starting of a pod VM via the VIC portlayer and persona server. Image store handling via the VIC persona server. This provider currently requires the feature/wolfpack branch of VIC. * Added pod stop and delete. Also added node capacity. Added the ability to stop and delete pod VMs via VIC. Also retrieve node capacity information from the VCH. * Cleanup and readme file Some file clean up and added a Readme.md markdown file for the VIC provider. * Cleaned up errors, added function comments, moved operation code 1. Cleaned up error handling. Set standard for creating errors. 2. Added method prototype comments for all interface functions. 3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder. * Add mocking code and unit tests for podcache, podcreator, and podstarter Used the unit test framework used in VIC to handle assertions in the provider's unit test. Mocking code generated using OSS project mockery, which is compatible with the testify assertion framework. * Vendored packages for the VIC provider Requires feature/wolfpack branch of VIC and a few specific commit sha of projects used within VIC. * Implementation of POD Stopper and Deleter unit tests (#4) * Updated files for initial PR
This commit is contained in:
25
vendor/github.com/tchap/go-patricia/.gitignore
generated
vendored
Normal file
25
vendor/github.com/tchap/go-patricia/.gitignore
generated
vendored
Normal file
@@ -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
|
||||
3
vendor/github.com/tchap/go-patricia/AUTHORS
generated
vendored
Normal file
3
vendor/github.com/tchap/go-patricia/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
This is the complete list of go-patricia copyright holders:
|
||||
|
||||
Ondřej Kupka <ondra.cap@gmail.com>
|
||||
20
vendor/github.com/tchap/go-patricia/LICENSE
generated
vendored
Normal file
20
vendor/github.com/tchap/go-patricia/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||
123
vendor/github.com/tchap/go-patricia/README.md
generated
vendored
Normal file
123
vendor/github.com/tchap/go-patricia/README.md
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
# go-patricia #
|
||||
|
||||
**Documentation**: [GoDoc](http://godoc.org/github.com/tchap/go-patricia/patricia)<br />
|
||||
**Build Status**: [](https://drone.io/github.com/tchap/go-patricia/latest)<br />
|
||||
**Test Coverage**: [](https://coveralls.io/r/tchap/go-patricia)
|
||||
|
||||
## 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"
|
||||
```
|
||||
|
||||
You can as well use gopkg.in thingie:
|
||||
|
||||
```go
|
||||
import "gopkg.in/tchap/go-patricia.v2/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 default trie (using the default parameter values).
|
||||
trie := NewTrie()
|
||||
|
||||
// Create a new custom trie.
|
||||
trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10))
|
||||
|
||||
// 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 in alphabetical order.
|
||||
trie.Visit(printItem)
|
||||
// "Karel Hynek Macha": 4
|
||||
// "Karel Macha": 3
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// 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)
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// Delete a subtree.
|
||||
trie.DeleteSubtree(Prefix("Pepa"))
|
||||
|
||||
// Print what is left.
|
||||
trie.Visit(printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
```
|
||||
|
||||
## License ##
|
||||
|
||||
MIT, check the `LICENSE` file.
|
||||
|
||||
[](https://www.gittip.com/tchap/
|
||||
"Gittip Badge")
|
||||
|
||||
[](https://bitdeli.com/free
|
||||
"Bitdeli Badge")
|
||||
325
vendor/github.com/tchap/go-patricia/patricia/children.go
generated
vendored
Normal file
325
vendor/github.com/tchap/go-patricia/patricia/children.go
generated
vendored
Normal file
@@ -0,0 +1,325 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type childList interface {
|
||||
length() int
|
||||
head() *Trie
|
||||
add(child *Trie) childList
|
||||
remove(b byte)
|
||||
replace(b byte, child *Trie)
|
||||
next(b byte) *Trie
|
||||
walk(prefix *Prefix, visitor VisitorFunc) error
|
||||
print(w io.Writer, indent int)
|
||||
total() int
|
||||
}
|
||||
|
||||
type tries []*Trie
|
||||
|
||||
func (t tries) Len() int {
|
||||
return len(t)
|
||||
}
|
||||
|
||||
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{
|
||||
children: make(tries, 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) remove(b byte) {
|
||||
for i, node := range list.children {
|
||||
if node.prefix[0] == b {
|
||||
list.children[i] = list.children[len(list.children)-1]
|
||||
list.children[len(list.children)-1] = nil
|
||||
list.children = list.children[:len(list.children)-1]
|
||||
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 {
|
||||
list.children[i] = child
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
sort.Sort(list.children)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
numChildren int
|
||||
headIndex 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: min,
|
||||
max: max,
|
||||
numChildren: list.length() + 1,
|
||||
headIndex: 0,
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
|
||||
func (list *denseChildList) length() int {
|
||||
return list.numChildren
|
||||
}
|
||||
|
||||
func (list *denseChildList) head() *Trie {
|
||||
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")
|
||||
}
|
||||
i = b - list.min
|
||||
list.children[i] = child
|
||||
|
||||
case b < list.min:
|
||||
children := make([]*Trie, list.max-b+1)
|
||||
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)
|
||||
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) 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 {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
594
vendor/github.com/tchap/go-patricia/patricia/patricia.go
generated
vendored
Normal file
594
vendor/github.com/tchap/go-patricia/patricia/patricia.go
generated
vendored
Normal file
@@ -0,0 +1,594 @@
|
||||
// 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"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Trie
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
DefaultMaxPrefixPerNode = 10
|
||||
DefaultMaxChildrenPerSparseNode = 8
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
maxPrefixPerNode int
|
||||
maxChildrenPerSparseNode int
|
||||
|
||||
children childList
|
||||
}
|
||||
|
||||
// Public API ------------------------------------------------------------------
|
||||
|
||||
type Option func(*Trie)
|
||||
|
||||
// Trie constructor.
|
||||
func NewTrie(options ...Option) *Trie {
|
||||
trie := &Trie{}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// in alphabetical order.
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
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.
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
// Delete the item.
|
||||
node.item = nil
|
||||
|
||||
// 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
|
||||
} 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.reset()
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise remove the root node from its parent.
|
||||
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 {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
var (
|
||||
common int
|
||||
node *Trie = trie
|
||||
child *Trie
|
||||
)
|
||||
|
||||
if node.prefix == nil {
|
||||
if len(key) <= trie.maxPrefixPerNode {
|
||||
node.prefix = key
|
||||
goto InsertItem
|
||||
}
|
||||
node.prefix = key[:trie.maxPrefixPerNode]
|
||||
key = key[trie.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) <= trie.maxPrefixPerNode {
|
||||
child.prefix = key
|
||||
node.children = node.children.add(child)
|
||||
node = child
|
||||
goto InsertItem
|
||||
} else {
|
||||
child.prefix = key[:trie.maxPrefixPerNode]
|
||||
key = key[trie.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) > trie.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) 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.
|
||||
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
|
||||
}
|
||||
|
||||
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 (
|
||||
SkipSubtree = errors.New("Skip this subtree")
|
||||
ErrNilPrefix = errors.New("Nil prefix passed into a method call")
|
||||
)
|
||||
218
vendor/github.com/tchap/go-patricia/patricia/patricia_dense_test.go
generated
vendored
Normal file
218
vendor/github.com/tchap/go-patricia/patricia/patricia_dense_test.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
// 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 (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
// Tests -----------------------------------------------------------------------
|
||||
|
||||
// overhead is allowed tolerance for Go's runtime/GC to increase the allocated memory
|
||||
// (to avoid failing tests on insignificant growth amounts)
|
||||
const overhead = 4000
|
||||
|
||||
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 <= DefaultMaxChildrenPerSparseNode; 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_DeleteLeakageDense(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
genTestData := func() *testData {
|
||||
// Generate a random hash as a key.
|
||||
key := uuid.NewV4()
|
||||
return &testData{key: key.String(), value: "v", retVal: success}
|
||||
}
|
||||
|
||||
testSize := 100
|
||||
data := make([]*testData, 0, testSize)
|
||||
for i := 0; i < testSize; i++ {
|
||||
data = append(data, genTestData())
|
||||
}
|
||||
|
||||
oldBytes := heapAllocatedBytes()
|
||||
|
||||
// repeat insertion/deletion for 10K times to catch possible memory issues
|
||||
for i := 0; i < 10000; i++ {
|
||||
for _, v := range data {
|
||||
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 {
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if newBytes := heapAllocatedBytes(); newBytes > oldBytes+overhead {
|
||||
t.Logf("Size=%d, Total=%d, Trie state:\n%s\n", trie.size(), trie.total(), trie.dump())
|
||||
t.Errorf("Heap space leak, grew %d bytes (%d to %d)\n", newBytes-oldBytes, oldBytes, newBytes)
|
||||
}
|
||||
|
||||
if numChildren := trie.children.length(); numChildren != 0 {
|
||||
t.Errorf("Trie is not empty: %v children found", numChildren)
|
||||
}
|
||||
}
|
||||
|
||||
func heapAllocatedBytes() uint64 {
|
||||
runtime.GC()
|
||||
|
||||
ms := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&ms)
|
||||
return ms.Alloc
|
||||
}
|
||||
690
vendor/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go
generated
vendored
Normal file
690
vendor/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go
generated
vendored
Normal file
@@ -0,0 +1,690 @@
|
||||
// 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, %v != 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) == 3 {
|
||||
return someErr
|
||||
}
|
||||
if item.(int) != 3 {
|
||||
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_DeleteLeakageSparse(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
data := []testData{
|
||||
{"Pepan", "Pepan Zdepan", success},
|
||||
{"Honza", "Honza Novak", success},
|
||||
{"Jenik", "Jenik Poustevnicek", success},
|
||||
}
|
||||
|
||||
oldBytes := heapAllocatedBytes()
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
for _, v := range data {
|
||||
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 {
|
||||
if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if newBytes := heapAllocatedBytes(); newBytes > oldBytes+overhead {
|
||||
t.Logf("Size=%d, Total=%d, Trie state:\n%s\n", trie.size(), trie.total(), trie.dump())
|
||||
t.Errorf("Heap space leak, grew %d bytes (from %d to %d)\n", newBytes-oldBytes, oldBytes, newBytes)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
// "Karel Hynek Macha": 4
|
||||
// "Karel Macha": 3
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// 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)
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// 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
|
||||
// "Karel Hynek Macha": 4
|
||||
// "Karel Macha": 3
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Pepa Sindelar": 2
|
||||
// "Karel Hynek Macha": 10
|
||||
}
|
||||
|
||||
// Helpers ---------------------------------------------------------------------
|
||||
|
||||
func printItem(prefix Prefix, item Item) error {
|
||||
fmt.Printf("%q: %v\n", prefix, item)
|
||||
return nil
|
||||
}
|
||||
131
vendor/github.com/tchap/go-patricia/patricia/patricia_test.go
generated
vendored
Normal file
131
vendor/github.com/tchap/go-patricia/patricia/patricia_test.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
// 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_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) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure Delete that affects the root node works.
|
||||
// This was panicking when Delete was broken.
|
||||
func TestTrie_DeleteRoot(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
v := testData{"aba", 0, success}
|
||||
|
||||
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("DELETE prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if ok := trie.Delete(Prefix(v.key)); ok != v.retVal {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrie_DeleteAbsentPrefix(t *testing.T) {
|
||||
trie := NewTrie()
|
||||
|
||||
v := testData{"a", 0, success}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
d := "ab"
|
||||
t.Logf("DELETE prefix=%v, success=%v", d, failure)
|
||||
if ok := trie.Delete(Prefix(d)); ok != failure {
|
||||
t.Errorf("Unexpected return value, expected=%v, got=%v", failure, ok)
|
||||
}
|
||||
t.Logf("GET prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
|
||||
if i := trie.Get(Prefix(v.key)); i != v.value {
|
||||
t.Errorf("Unexpected item, expected=%v, got=%v", v.value, i)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user