Browse code

refactor TruncIndex to use a trie & vendor deps

Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)

unclejack authored on 2014/06/25 07:24:02
Showing 14 changed files
... ...
@@ -182,11 +182,7 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool, con
182 182
 
183 183
 	// don't update the Suffixarray if we're starting up
184 184
 	// we'll waste time if we update it for every container
185
-	if updateSuffixarray {
186
-		daemon.idIndex.Add(container.ID)
187
-	} else {
188
-		daemon.idIndex.AddWithoutSuffixarrayUpdate(container.ID)
189
-	}
185
+	daemon.idIndex.Add(container.ID)
190 186
 
191 187
 	// FIXME: if the container is supposed to be running but is not, auto restart it?
192 188
 	//        if so, then we need to restart monitor and init a new lock
... ...
@@ -377,8 +373,6 @@ func (daemon *Daemon) restore() error {
377 377
 		}
378 378
 	}
379 379
 
380
-	daemon.idIndex.UpdateSuffixarray()
381
-
382 380
 	for _, container := range containersToStart {
383 381
 		utils.Debugf("Starting container %d", container.ID)
384 382
 		if err := container.Start(); err != nil {
... ...
@@ -47,6 +47,8 @@ clone git github.com/gorilla/mux 136d54f81f
47 47
 
48 48
 clone git github.com/syndtr/gocapability 3c85049eae
49 49
 
50
+clone git github.com/tchap/go-patricia v1.0.0 
51
+
50 52
 clone hg code.google.com/p/go.net 84a4013f96e0
51 53
 
52 54
 clone hg code.google.com/p/gosqlite 74691fb6f837
... ...
@@ -1,31 +1,34 @@
1 1
 package truncindex
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"fmt"
5
-	"index/suffixarray"
6 6
 	"strings"
7 7
 	"sync"
8
+
9
+	"github.com/tchap/go-patricia/patricia"
10
+)
11
+
12
+var (
13
+	ErrNoID = errors.New("prefix can't be empty")
8 14
 )
9 15
 
10 16
 // TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
11 17
 // This is used to retrieve image and container IDs by more convenient shorthand prefixes.
12 18
 type TruncIndex struct {
13 19
 	sync.RWMutex
14
-	index *suffixarray.Index
15
-	ids   map[string]bool
16
-	bytes []byte
20
+	trie *patricia.Trie
21
+	ids  map[string]struct{}
17 22
 }
18 23
 
19 24
 func NewTruncIndex(ids []string) (idx *TruncIndex) {
20 25
 	idx = &TruncIndex{
21
-		ids:   make(map[string]bool),
22
-		bytes: []byte{' '},
26
+		ids:  make(map[string]struct{}),
27
+		trie: patricia.NewTrie(),
23 28
 	}
24 29
 	for _, id := range ids {
25
-		idx.ids[id] = true
26
-		idx.bytes = append(idx.bytes, []byte(id+" ")...)
30
+		idx.addId(id)
27 31
 	}
28
-	idx.index = suffixarray.New(idx.bytes)
29 32
 	return
30 33
 }
31 34
 
... ...
@@ -33,11 +36,16 @@ func (idx *TruncIndex) addId(id string) error {
33 33
 	if strings.Contains(id, " ") {
34 34
 		return fmt.Errorf("Illegal character: ' '")
35 35
 	}
36
+	if id == "" {
37
+		return ErrNoID
38
+	}
36 39
 	if _, exists := idx.ids[id]; exists {
37
-		return fmt.Errorf("Id already exists: %s", id)
40
+		return fmt.Errorf("Id already exists: '%s'", id)
41
+	}
42
+	idx.ids[id] = struct{}{}
43
+	if inserted := idx.trie.Insert(patricia.Prefix(id), struct{}{}); !inserted {
44
+		return fmt.Errorf("Failed to insert id: %s", id)
38 45
 	}
39
-	idx.ids[id] = true
40
-	idx.bytes = append(idx.bytes, []byte(id+" ")...)
41 46
 	return nil
42 47
 }
43 48
 
... ...
@@ -47,56 +55,46 @@ func (idx *TruncIndex) Add(id string) error {
47 47
 	if err := idx.addId(id); err != nil {
48 48
 		return err
49 49
 	}
50
-	idx.index = suffixarray.New(idx.bytes)
51 50
 	return nil
52 51
 }
53 52
 
54
-func (idx *TruncIndex) AddWithoutSuffixarrayUpdate(id string) error {
55
-	idx.Lock()
56
-	defer idx.Unlock()
57
-	return idx.addId(id)
58
-}
59
-
60
-func (idx *TruncIndex) UpdateSuffixarray() {
61
-	idx.Lock()
62
-	defer idx.Unlock()
63
-	idx.index = suffixarray.New(idx.bytes)
64
-}
65
-
66 53
 func (idx *TruncIndex) Delete(id string) error {
67 54
 	idx.Lock()
68 55
 	defer idx.Unlock()
69
-	if _, exists := idx.ids[id]; !exists {
70
-		return fmt.Errorf("No such id: %s", id)
71
-	}
72
-	before, after, err := idx.lookup(id)
73
-	if err != nil {
74
-		return err
56
+	if _, exists := idx.ids[id]; !exists || id == "" {
57
+		return fmt.Errorf("No such id: '%s'", id)
75 58
 	}
76 59
 	delete(idx.ids, id)
77
-	idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...)
78
-	idx.index = suffixarray.New(idx.bytes)
79
-	return nil
80
-}
81
-
82
-func (idx *TruncIndex) lookup(s string) (int, int, error) {
83
-	offsets := idx.index.Lookup([]byte(" "+s), -1)
84
-	//log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
85
-	if offsets == nil || len(offsets) == 0 || len(offsets) > 1 {
86
-		return -1, -1, fmt.Errorf("No such id: %s", s)
60
+	if deleted := idx.trie.Delete(patricia.Prefix(id)); !deleted {
61
+		return fmt.Errorf("No such id: '%s'", id)
87 62
 	}
88
-	offsetBefore := offsets[0] + 1
89
-	offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ")
90
-	return offsetBefore, offsetAfter, nil
63
+	return nil
91 64
 }
92 65
 
93 66
 func (idx *TruncIndex) Get(s string) (string, error) {
94 67
 	idx.RLock()
95 68
 	defer idx.RUnlock()
96
-	before, after, err := idx.lookup(s)
97
-	//log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
98
-	if err != nil {
99
-		return "", err
69
+	var (
70
+		id string
71
+	)
72
+	if s == "" {
73
+		return "", ErrNoID
74
+	}
75
+	subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
76
+		if id != "" {
77
+			// we haven't found the ID if there are two or more IDs
78
+			id = ""
79
+			return fmt.Errorf("we've found two entries")
80
+		}
81
+		id = string(prefix)
82
+		return nil
83
+	}
84
+
85
+	if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
86
+		return "", fmt.Errorf("No such id: %s", s)
87
+	}
88
+	if id != "" {
89
+		return id, nil
100 90
 	}
101
-	return string(idx.bytes[before:after]), err
91
+	return "", fmt.Errorf("No such id: %s", s)
102 92
 }
... ...
@@ -26,8 +26,16 @@ func TestTruncIndex(t *testing.T) {
26 26
 	if err := index.Add(id); err != nil {
27 27
 		t.Fatal(err)
28 28
 	}
29
+
30
+	// Add an empty id (should fail)
31
+	if err := index.Add(""); err == nil {
32
+		t.Fatalf("Adding an empty id should return an error")
33
+	}
34
+
29 35
 	// Get a non-existing id
30 36
 	assertIndexGet(t, index, "abracadabra", "", true)
37
+	// Get an empty id
38
+	assertIndexGet(t, index, "", "", true)
31 39
 	// Get the exact id
32 40
 	assertIndexGet(t, index, id, id, false)
33 41
 	// The first letter should match
... ...
@@ -60,6 +68,11 @@ func TestTruncIndex(t *testing.T) {
60 60
 		t.Fatalf("Deleting a non-existing id should return an error")
61 61
 	}
62 62
 
63
+	// Deleting an empty id should return an error
64
+	if err := index.Delete(""); err == nil {
65
+		t.Fatal("Deleting an empty id should return an error")
66
+	}
67
+
63 68
 	// Deleting id2 should remove conflicts
64 69
 	if err := index.Delete(id2); err != nil {
65 70
 		t.Fatal(err)
... ...
@@ -84,7 +97,7 @@ func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult strin
84 84
 	if result, err := index.Get(input); err != nil && !expectError {
85 85
 		t.Fatalf("Unexpected error getting '%s': %s", input, err)
86 86
 	} else if err == nil && expectError {
87
-		t.Fatalf("Getting '%s' should return an error", input)
87
+		t.Fatalf("Getting '%s' should return an error, not '%s'", input, result)
88 88
 	} else if result != expectedResult {
89 89
 		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
90 90
 	}
91 91
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+# Swap files.
1
+*.swp
2
+
3
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
4
+*.o
5
+*.a
6
+*.so
7
+
8
+# Folders
9
+_obj
10
+_test
11
+
12
+# Architecture specific extensions/prefixes
13
+*.[568vq]
14
+[568vq].out
15
+
16
+*.cgo1.go
17
+*.cgo2.c
18
+_cgo_defun.c
19
+_cgo_gotypes.go
20
+_cgo_export.*
21
+
22
+_testmain.go
23
+
24
+*.exe
0 25
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+language: go
1
+
2
+go:
3
+  - 1.2
4
+  - tip
5
+
6
+branches:
7
+  exclude:
8
+    - wip
0 9
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+This is the complete list of go-patricia copyright holders:
1
+
2
+Ondřej Kupka <ondra.cap@gmail.com>
0 3
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+The MIT License (MIT)
1
+
2
+Copyright (c) 2014 The AUTHORS
3
+
4
+Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+this software and associated documentation files (the "Software"), to deal in
6
+the Software without restriction, including without limitation the rights to
7
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+the Software, and to permit persons to whom the Software is furnished to do so,
9
+subject to the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be included in all
12
+copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0 20
new file mode 100644
... ...
@@ -0,0 +1,112 @@
0
+# go-patricia #
1
+
2
+**Documentation**: [GoDoc](http://godoc.org/github.com/tchap/go-patricia/patricia)<br />
3
+**Build Status**: [![Build Status](https://travis-ci.org/tchap/go-patricia.png?branch=master)](https://travis-ci.org/tchap/go-patricia)<br >
4
+**Test Coverage**: Comming as soon as Drone.io people update their Go.
5
+
6
+## About ##
7
+
8
+A generic patricia trie (also called radix tree) implemented in Go (Golang).
9
+
10
+The patricia trie as implemented in this library enables fast visiting of items
11
+in some particular ways:
12
+
13
+1. visit all items saved in the tree,
14
+2. visit all items matching particular prefix (visit subtree), or
15
+3. given a string, visit all items matching some prefix of that string.
16
+
17
+`[]byte` type is used for keys, `interface{}` for values.
18
+
19
+`Trie` is not thread safe. Synchronize the access yourself.
20
+
21
+### State of the Project ###
22
+
23
+Apparently some people are using this, so the API should not change often.
24
+Any ideas on how to make the library better are still welcome.
25
+
26
+More (unit) testing would be cool as well...
27
+
28
+## Usage ##
29
+
30
+Import the package from GitHub first.
31
+
32
+```go
33
+import "github.com/tchap/go-patricia/patricia"
34
+```
35
+
36
+Then you can start having fun.
37
+
38
+```go
39
+printItem := func(prefix patricia.Prefix, item patricia.Item) error {
40
+	fmt.Printf("%q: %v\n", prefix, item)
41
+	return nil
42
+}
43
+
44
+// Create a new tree.
45
+trie := NewTrie()
46
+
47
+// Insert some items.
48
+trie.Insert(Prefix("Pepa Novak"), 1)
49
+trie.Insert(Prefix("Pepa Sindelar"), 2)
50
+trie.Insert(Prefix("Karel Macha"), 3)
51
+trie.Insert(Prefix("Karel Hynek Macha"), 4)
52
+
53
+// Just check if some things are present in the tree.
54
+key := Prefix("Pepa Novak")
55
+fmt.Printf("%q present? %v\n", key, trie.Match(key))
56
+// "Pepa Novak" present? true
57
+key = Prefix("Karel")
58
+fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
59
+// Anybody called "Karel" here? true
60
+
61
+// Walk the tree.
62
+trie.Visit(printItem)
63
+// "Pepa Novak": 1
64
+// "Pepa Sindelar": 2
65
+// "Karel Macha": 3
66
+// "Karel Hynek Macha": 4
67
+
68
+// Walk a subtree.
69
+trie.VisitSubtree(Prefix("Pepa"), printItem)
70
+// "Pepa Novak": 1
71
+// "Pepa Sindelar": 2
72
+
73
+// Modify an item, then fetch it from the tree.
74
+trie.Set(Prefix("Karel Hynek Macha"), 10)
75
+key = Prefix("Karel Hynek Macha")
76
+fmt.Printf("%q: %v\n", key, trie.Get(key))
77
+// "Karel Hynek Macha": 10
78
+
79
+// Walk prefixes.
80
+prefix := Prefix("Karel Hynek Macha je kouzelnik")
81
+trie.VisitPrefixes(prefix, printItem)
82
+// "Karel Hynek Macha": 10
83
+
84
+// Delete some items.
85
+trie.Delete(Prefix("Pepa Novak"))
86
+trie.Delete(Prefix("Karel Macha"))
87
+
88
+// Walk again.
89
+trie.Visit(printItem)
90
+// "Pepa Sindelar": 2
91
+// "Karel Hynek Macha": 10
92
+
93
+// Delete a subtree.
94
+trie.DeleteSubtree(Prefix("Pepa"))
95
+
96
+// Print what is left.
97
+trie.Visit(printItem)
98
+// "Karel Hynek Macha": 10
99
+```
100
+
101
+## License ##
102
+
103
+MIT, check the `LICENSE` file.
104
+
105
+[![Gittip
106
+Badge](http://img.shields.io/gittip/alanhamlett.png)](https://www.gittip.com/tchap/
107
+"Gittip Badge")
108
+
109
+[![Bitdeli
110
+Badge](https://d2weczhvl823v0.cloudfront.net/tchap/go-patricia/trend.png)](https://bitdeli.com/free
111
+"Bitdeli Badge")
0 112
new file mode 100644
... ...
@@ -0,0 +1,231 @@
0
+// Copyright (c) 2014 The go-patricia AUTHORS
1
+//
2
+// Use of this source code is governed by The MIT License
3
+// that can be found in the LICENSE file.
4
+
5
+package patricia
6
+
7
+const (
8
+	// Max prefix length that is kept in a single trie node.
9
+	MaxPrefixPerNode = 10
10
+	// Max children to keep in a node in the sparse mode.
11
+	MaxChildrenPerSparseNode = 8
12
+)
13
+
14
+type childList interface {
15
+	length() int
16
+	head() *Trie
17
+	add(child *Trie) childList
18
+	replace(b byte, child *Trie)
19
+	remove(child *Trie)
20
+	next(b byte) *Trie
21
+	walk(prefix *Prefix, visitor VisitorFunc) error
22
+}
23
+
24
+type sparseChildList struct {
25
+	children []*Trie
26
+}
27
+
28
+func newSparseChildList() childList {
29
+	return &sparseChildList{
30
+		children: make([]*Trie, 0, MaxChildrenPerSparseNode),
31
+	}
32
+}
33
+
34
+func (list *sparseChildList) length() int {
35
+	return len(list.children)
36
+}
37
+
38
+func (list *sparseChildList) head() *Trie {
39
+	return list.children[0]
40
+}
41
+
42
+func (list *sparseChildList) add(child *Trie) childList {
43
+	// Search for an empty spot and insert the child if possible.
44
+	if len(list.children) != cap(list.children) {
45
+		list.children = append(list.children, child)
46
+		return list
47
+	}
48
+
49
+	// Otherwise we have to transform to the dense list type.
50
+	return newDenseChildList(list, child)
51
+}
52
+
53
+func (list *sparseChildList) replace(b byte, child *Trie) {
54
+	// Seek the child and replace it.
55
+	for i, node := range list.children {
56
+		if node.prefix[0] == b {
57
+			list.children[i] = child
58
+			return
59
+		}
60
+	}
61
+}
62
+
63
+func (list *sparseChildList) remove(child *Trie) {
64
+	for i, node := range list.children {
65
+		if node.prefix[0] == child.prefix[0] {
66
+			list.children = append(list.children[:i], list.children[i+1:]...)
67
+			return
68
+		}
69
+	}
70
+
71
+	// This is not supposed to be reached.
72
+	panic("removing non-existent child")
73
+}
74
+
75
+func (list *sparseChildList) next(b byte) *Trie {
76
+	for _, child := range list.children {
77
+		if child.prefix[0] == b {
78
+			return child
79
+		}
80
+	}
81
+	return nil
82
+}
83
+
84
+func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
85
+	for _, child := range list.children {
86
+		*prefix = append(*prefix, child.prefix...)
87
+		if child.item != nil {
88
+			err := visitor(*prefix, child.item)
89
+			if err != nil {
90
+				if err == SkipSubtree {
91
+					*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
92
+					continue
93
+				}
94
+				*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
95
+				return err
96
+			}
97
+		}
98
+
99
+		err := child.children.walk(prefix, visitor)
100
+		*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
101
+		if err != nil {
102
+			return err
103
+		}
104
+	}
105
+
106
+	return nil
107
+}
108
+
109
+type denseChildList struct {
110
+	min      int
111
+	max      int
112
+	children []*Trie
113
+}
114
+
115
+func newDenseChildList(list *sparseChildList, child *Trie) childList {
116
+	var (
117
+		min int = 255
118
+		max int = 0
119
+	)
120
+	for _, child := range list.children {
121
+		b := int(child.prefix[0])
122
+		if b < min {
123
+			min = b
124
+		}
125
+		if b > max {
126
+			max = b
127
+		}
128
+	}
129
+
130
+	b := int(child.prefix[0])
131
+	if b < min {
132
+		min = b
133
+	}
134
+	if b > max {
135
+		max = b
136
+	}
137
+
138
+	children := make([]*Trie, max-min+1)
139
+	for _, child := range list.children {
140
+		children[int(child.prefix[0])-min] = child
141
+	}
142
+	children[int(child.prefix[0])-min] = child
143
+
144
+	return &denseChildList{min, max, children}
145
+}
146
+
147
+func (list *denseChildList) length() int {
148
+	return list.max - list.min + 1
149
+}
150
+
151
+func (list *denseChildList) head() *Trie {
152
+	return list.children[0]
153
+}
154
+
155
+func (list *denseChildList) add(child *Trie) childList {
156
+	b := int(child.prefix[0])
157
+
158
+	switch {
159
+	case list.min <= b && b <= list.max:
160
+		if list.children[b-list.min] != nil {
161
+			panic("dense child list collision detected")
162
+		}
163
+		list.children[b-list.min] = child
164
+
165
+	case b < list.min:
166
+		children := make([]*Trie, list.max-b+1)
167
+		children[0] = child
168
+		copy(children[list.min-b:], list.children)
169
+		list.children = children
170
+		list.min = b
171
+
172
+	default: // b > list.max
173
+		children := make([]*Trie, b-list.min+1)
174
+		children[b-list.min] = child
175
+		copy(children, list.children)
176
+		list.children = children
177
+		list.max = b
178
+	}
179
+
180
+	return list
181
+}
182
+
183
+func (list *denseChildList) replace(b byte, child *Trie) {
184
+	list.children[int(b)-list.min] = nil
185
+	list.children[int(child.prefix[0])-list.min] = child
186
+}
187
+
188
+func (list *denseChildList) remove(child *Trie) {
189
+	i := int(child.prefix[0]) - list.min
190
+	if list.children[i] == nil {
191
+		// This is not supposed to be reached.
192
+		panic("removing non-existent child")
193
+	}
194
+	list.children[i] = nil
195
+}
196
+
197
+func (list *denseChildList) next(b byte) *Trie {
198
+	i := int(b)
199
+	if i < list.min || list.max < i {
200
+		return nil
201
+	}
202
+	return list.children[i-list.min]
203
+}
204
+
205
+func (list *denseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
206
+	for _, child := range list.children {
207
+		if child == nil {
208
+			continue
209
+		}
210
+		*prefix = append(*prefix, child.prefix...)
211
+		if child.item != nil {
212
+			if err := visitor(*prefix, child.item); err != nil {
213
+				if err == SkipSubtree {
214
+					*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
215
+					continue
216
+				}
217
+				*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
218
+				return err
219
+			}
220
+		}
221
+
222
+		err := child.children.walk(prefix, visitor)
223
+		*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
224
+		if err != nil {
225
+			return err
226
+		}
227
+	}
228
+
229
+	return nil
230
+}
0 231
new file mode 100644
... ...
@@ -0,0 +1,432 @@
0
+// Copyright (c) 2014 The go-patricia AUTHORS
1
+//
2
+// Use of this source code is governed by The MIT License
3
+// that can be found in the LICENSE file.
4
+
5
+package patricia
6
+
7
+import (
8
+	"errors"
9
+)
10
+
11
+//------------------------------------------------------------------------------
12
+// Trie
13
+//------------------------------------------------------------------------------
14
+
15
+type (
16
+	Prefix      []byte
17
+	Item        interface{}
18
+	VisitorFunc func(prefix Prefix, item Item) error
19
+)
20
+
21
+// Trie is a generic patricia trie that allows fast retrieval of items by prefix.
22
+// and other funky stuff.
23
+//
24
+// Trie is not thread-safe.
25
+type Trie struct {
26
+	prefix Prefix
27
+	item   Item
28
+
29
+	children childList
30
+}
31
+
32
+// Public API ------------------------------------------------------------------
33
+
34
+// Trie constructor.
35
+func NewTrie() *Trie {
36
+	return &Trie{
37
+		children: newSparseChildList(),
38
+	}
39
+}
40
+
41
+// Item returns the item stored in the root of this trie.
42
+func (trie *Trie) Item() Item {
43
+	return trie.item
44
+}
45
+
46
+// Insert inserts a new item into the trie using the given prefix. Insert does
47
+// not replace existing items. It returns false if an item was already in place.
48
+func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) {
49
+	return trie.put(key, item, false)
50
+}
51
+
52
+// Set works much like Insert, but it always sets the item, possibly replacing
53
+// the item previously inserted.
54
+func (trie *Trie) Set(key Prefix, item Item) {
55
+	trie.put(key, item, true)
56
+}
57
+
58
+// Get returns the item located at key.
59
+//
60
+// This method is a bit dangerous, because Get can as well end up in an internal
61
+// node that is not really representing any user-defined value. So when nil is
62
+// a valid value being used, it is not possible to tell if the value was inserted
63
+// into the tree by the user or not. A possible workaround for this is not to use
64
+// nil interface as a valid value, even using zero value of any type is enough
65
+// to prevent this bad behaviour.
66
+func (trie *Trie) Get(key Prefix) (item Item) {
67
+	_, node, found, leftover := trie.findSubtree(key)
68
+	if !found || len(leftover) != 0 {
69
+		return nil
70
+	}
71
+	return node.item
72
+}
73
+
74
+// Match returns what Get(prefix) != nil would return. The same warning as for
75
+// Get applies here as well.
76
+func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) {
77
+	return trie.Get(prefix) != nil
78
+}
79
+
80
+// MatchSubtree returns true when there is a subtree representing extensions
81
+// to key, that is if there are any keys in the tree which have key as prefix.
82
+func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
83
+	_, _, matched, _ = trie.findSubtree(key)
84
+	return
85
+}
86
+
87
+// Visit calls visitor on every node containing a non-nil item.
88
+//
89
+// If an error is returned from visitor, the function stops visiting the tree
90
+// and returns that error, unless it is a special error - SkipSubtree. In that
91
+// case Visit skips the subtree represented by the current node and continues
92
+// elsewhere.
93
+func (trie *Trie) Visit(visitor VisitorFunc) error {
94
+	return trie.walk(nil, visitor)
95
+}
96
+
97
+// VisitSubtree works much like Visit, but it only visits nodes matching prefix.
98
+func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error {
99
+	// Nil prefix not allowed.
100
+	if prefix == nil {
101
+		panic(ErrNilPrefix)
102
+	}
103
+
104
+	// Empty trie must be handled explicitly.
105
+	if trie.prefix == nil {
106
+		return nil
107
+	}
108
+
109
+	// Locate the relevant subtree.
110
+	_, root, found, leftover := trie.findSubtree(prefix)
111
+	if !found {
112
+		return nil
113
+	}
114
+	prefix = append(prefix, leftover...)
115
+
116
+	// Visit it.
117
+	return root.walk(prefix, visitor)
118
+}
119
+
120
+// VisitPrefixes visits only nodes that represent prefixes of key.
121
+// To say the obvious, returning SkipSubtree from visitor makes no sense here.
122
+func (trie *Trie) VisitPrefixes(key Prefix, visitor VisitorFunc) error {
123
+	// Nil key not allowed.
124
+	if key == nil {
125
+		panic(ErrNilPrefix)
126
+	}
127
+
128
+	// Empty trie must be handled explicitly.
129
+	if trie.prefix == nil {
130
+		return nil
131
+	}
132
+
133
+	// Walk the path matching key prefixes.
134
+	node := trie
135
+	prefix := key
136
+	offset := 0
137
+	for {
138
+		// Compute what part of prefix matches.
139
+		common := node.longestCommonPrefixLength(key)
140
+		key = key[common:]
141
+		offset += common
142
+
143
+		// Partial match means that there is no subtree matching prefix.
144
+		if common < len(node.prefix) {
145
+			return nil
146
+		}
147
+
148
+		// Call the visitor.
149
+		if item := node.item; item != nil {
150
+			if err := visitor(prefix[:offset], item); err != nil {
151
+				return err
152
+			}
153
+		}
154
+
155
+		if len(key) == 0 {
156
+			// This node represents key, we are finished.
157
+			return nil
158
+		}
159
+
160
+		// There is some key suffix left, move to the children.
161
+		child := node.children.next(key[0])
162
+		if child == nil {
163
+			// There is nowhere to continue, return.
164
+			return nil
165
+		}
166
+
167
+		node = child
168
+	}
169
+}
170
+
171
+// Delete deletes the item represented by the given prefix.
172
+//
173
+// True is returned if the matching node was found and deleted.
174
+func (trie *Trie) Delete(key Prefix) (deleted bool) {
175
+	// Nil prefix not allowed.
176
+	if key == nil {
177
+		panic(ErrNilPrefix)
178
+	}
179
+
180
+	// Empty trie must be handled explicitly.
181
+	if trie.prefix == nil {
182
+		return false
183
+	}
184
+
185
+	// Find the relevant node.
186
+	parent, node, _, leftover := trie.findSubtree(key)
187
+	if len(leftover) != 0 {
188
+		return false
189
+	}
190
+
191
+	// If the item is already set to nil, there is nothing to do.
192
+	if node.item == nil {
193
+		return false
194
+	}
195
+
196
+	// Delete the item.
197
+	node.item = nil
198
+
199
+	// Compact since that might be possible now.
200
+	if compacted := node.compact(); compacted != node {
201
+		if parent == nil {
202
+			*node = *compacted
203
+		} else {
204
+			parent.children.replace(node.prefix[0], compacted)
205
+			*parent = *parent.compact()
206
+		}
207
+	}
208
+
209
+	return true
210
+}
211
+
212
+// DeleteSubtree finds the subtree exactly matching prefix and deletes it.
213
+//
214
+// True is returned if the subtree was found and deleted.
215
+func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
216
+	// Nil prefix not allowed.
217
+	if prefix == nil {
218
+		panic(ErrNilPrefix)
219
+	}
220
+
221
+	// Empty trie must be handled explicitly.
222
+	if trie.prefix == nil {
223
+		return false
224
+	}
225
+
226
+	// Locate the relevant subtree.
227
+	parent, root, found, _ := trie.findSubtree(prefix)
228
+	if !found {
229
+		return false
230
+	}
231
+
232
+	// If we are in the root of the trie, reset the trie.
233
+	if parent == nil {
234
+		root.prefix = nil
235
+		root.children = newSparseChildList()
236
+		return true
237
+	}
238
+
239
+	// Otherwise remove the root node from its parent.
240
+	parent.children.remove(root)
241
+	return true
242
+}
243
+
244
+// Internal helper methods -----------------------------------------------------
245
+
246
+func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
247
+	// Nil prefix not allowed.
248
+	if key == nil {
249
+		panic(ErrNilPrefix)
250
+	}
251
+
252
+	var (
253
+		common int
254
+		node   *Trie = trie
255
+		child  *Trie
256
+	)
257
+
258
+	if node.prefix == nil {
259
+		if len(key) <= MaxPrefixPerNode {
260
+			node.prefix = key
261
+			goto InsertItem
262
+		}
263
+		node.prefix = key[:MaxPrefixPerNode]
264
+		key = key[MaxPrefixPerNode:]
265
+		goto AppendChild
266
+	}
267
+
268
+	for {
269
+		// Compute the longest common prefix length.
270
+		common = node.longestCommonPrefixLength(key)
271
+		key = key[common:]
272
+
273
+		// Only a part matches, split.
274
+		if common < len(node.prefix) {
275
+			goto SplitPrefix
276
+		}
277
+
278
+		// common == len(node.prefix) since never (common > len(node.prefix))
279
+		// common == len(former key) <-> 0 == len(key)
280
+		// -> former key == node.prefix
281
+		if len(key) == 0 {
282
+			goto InsertItem
283
+		}
284
+
285
+		// Check children for matching prefix.
286
+		child = node.children.next(key[0])
287
+		if child == nil {
288
+			goto AppendChild
289
+		}
290
+		node = child
291
+	}
292
+
293
+SplitPrefix:
294
+	// Split the prefix if necessary.
295
+	child = new(Trie)
296
+	*child = *node
297
+	*node = *NewTrie()
298
+	node.prefix = child.prefix[:common]
299
+	child.prefix = child.prefix[common:]
300
+	child = child.compact()
301
+	node.children = node.children.add(child)
302
+
303
+AppendChild:
304
+	// Keep appending children until whole prefix is inserted.
305
+	// This loop starts with empty node.prefix that needs to be filled.
306
+	for len(key) != 0 {
307
+		child := NewTrie()
308
+		if len(key) <= MaxPrefixPerNode {
309
+			child.prefix = key
310
+			node.children = node.children.add(child)
311
+			node = child
312
+			goto InsertItem
313
+		} else {
314
+			child.prefix = key[:MaxPrefixPerNode]
315
+			key = key[MaxPrefixPerNode:]
316
+			node.children = node.children.add(child)
317
+			node = child
318
+		}
319
+	}
320
+
321
+InsertItem:
322
+	// Try to insert the item if possible.
323
+	if replace || node.item == nil {
324
+		node.item = item
325
+		return true
326
+	}
327
+	return false
328
+}
329
+
330
+func (trie *Trie) compact() *Trie {
331
+	// Only a node with a single child can be compacted.
332
+	if trie.children.length() != 1 {
333
+		return trie
334
+	}
335
+
336
+	child := trie.children.head()
337
+
338
+	// If any item is set, we cannot compact since we want to retain
339
+	// the ability to do searching by key. This makes compaction less usable,
340
+	// but that simply cannot be avoided.
341
+	if trie.item != nil || child.item != nil {
342
+		return trie
343
+	}
344
+
345
+	// Make sure the combined prefixes fit into a single node.
346
+	if len(trie.prefix)+len(child.prefix) > MaxPrefixPerNode {
347
+		return trie
348
+	}
349
+
350
+	// Concatenate the prefixes, move the items.
351
+	child.prefix = append(trie.prefix, child.prefix...)
352
+	if trie.item != nil {
353
+		child.item = trie.item
354
+	}
355
+
356
+	return child
357
+}
358
+
359
+func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) {
360
+	// Find the subtree matching prefix.
361
+	root = trie
362
+	for {
363
+		// Compute what part of prefix matches.
364
+		common := root.longestCommonPrefixLength(prefix)
365
+		prefix = prefix[common:]
366
+
367
+		// We used up the whole prefix, subtree found.
368
+		if len(prefix) == 0 {
369
+			found = true
370
+			leftover = root.prefix[common:]
371
+			return
372
+		}
373
+
374
+		// Partial match means that there is no subtree matching prefix.
375
+		if common < len(root.prefix) {
376
+			leftover = root.prefix[common:]
377
+			return
378
+		}
379
+
380
+		// There is some prefix left, move to the children.
381
+		child := root.children.next(prefix[0])
382
+		if child == nil {
383
+			// There is nowhere to continue, there is no subtree matching prefix.
384
+			return
385
+		}
386
+
387
+		parent = root
388
+		root = child
389
+	}
390
+}
391
+
392
+func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error {
393
+	var prefix Prefix
394
+	// Allocate a bit more space for prefix at the beginning.
395
+	if actualRootPrefix == nil {
396
+		prefix = make(Prefix, 32+len(trie.prefix))
397
+		copy(prefix, trie.prefix)
398
+		prefix = prefix[:len(trie.prefix)]
399
+	} else {
400
+		prefix = make(Prefix, 32+len(actualRootPrefix))
401
+		copy(prefix, actualRootPrefix)
402
+		prefix = prefix[:len(actualRootPrefix)]
403
+	}
404
+
405
+	// Visit the root first. Not that this works for empty trie as well since
406
+	// in that case item == nil && len(children) == 0.
407
+	if trie.item != nil {
408
+		if err := visitor(prefix, trie.item); err != nil {
409
+			if err == SkipSubtree {
410
+				return nil
411
+			}
412
+			return err
413
+		}
414
+	}
415
+
416
+	// Then continue to the children.
417
+	return trie.children.walk(&prefix, visitor)
418
+}
419
+
420
+func (trie *Trie) longestCommonPrefixLength(prefix Prefix) (i int) {
421
+	for ; i < len(prefix) && i < len(trie.prefix) && prefix[i] == trie.prefix[i]; i++ {
422
+	}
423
+	return
424
+}
425
+
426
+// Errors ----------------------------------------------------------------------
427
+
428
+var (
429
+	SkipSubtree  = errors.New("Skip this subtree")
430
+	ErrNilPrefix = errors.New("Nil prefix passed into a method call")
431
+)
0 432
new file mode 100644
... ...
@@ -0,0 +1,161 @@
0
+// Copyright (c) 2014 The go-patricia AUTHORS
1
+//
2
+// Use of this source code is governed by The MIT License
3
+// that can be found in the LICENSE file.
4
+
5
+package patricia
6
+
7
+import (
8
+	"testing"
9
+)
10
+
11
+// Tests -----------------------------------------------------------------------
12
+
13
+func TestTrie_InsertDense(t *testing.T) {
14
+	trie := NewTrie()
15
+
16
+	data := []testData{
17
+		{"aba", 0, success},
18
+		{"abb", 1, success},
19
+		{"abc", 2, success},
20
+		{"abd", 3, success},
21
+		{"abe", 4, success},
22
+		{"abf", 5, success},
23
+		{"abg", 6, success},
24
+		{"abh", 7, success},
25
+		{"abi", 8, success},
26
+		{"abj", 9, success},
27
+		{"abk", 0, success},
28
+		{"abl", 1, success},
29
+		{"abm", 2, success},
30
+		{"abn", 3, success},
31
+		{"abo", 4, success},
32
+		{"abp", 5, success},
33
+		{"abq", 6, success},
34
+		{"abr", 7, success},
35
+		{"abs", 8, success},
36
+		{"abt", 9, success},
37
+		{"abu", 0, success},
38
+		{"abv", 1, success},
39
+		{"abw", 2, success},
40
+		{"abx", 3, success},
41
+		{"aby", 4, success},
42
+		{"abz", 5, success},
43
+	}
44
+
45
+	for _, v := range data {
46
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
47
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
48
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
49
+		}
50
+	}
51
+}
52
+
53
+func TestTrie_InsertDensePreceeding(t *testing.T) {
54
+	trie := NewTrie()
55
+	start := byte(70)
56
+	// create a dense node
57
+	for i := byte(0); i <= MaxChildrenPerSparseNode; i++ {
58
+		if !trie.Insert(Prefix([]byte{start + i}), true) {
59
+			t.Errorf("insert failed, prefix=%v", start+i)
60
+		}
61
+	}
62
+	// insert some preceeding keys
63
+	for i := byte(1); i < start; i *= i + 1 {
64
+		if !trie.Insert(Prefix([]byte{start - i}), true) {
65
+			t.Errorf("insert failed, prefix=%v", start-i)
66
+		}
67
+	}
68
+}
69
+
70
+func TestTrie_InsertDenseDuplicatePrefixes(t *testing.T) {
71
+	trie := NewTrie()
72
+
73
+	data := []testData{
74
+		{"aba", 0, success},
75
+		{"abb", 1, success},
76
+		{"abc", 2, success},
77
+		{"abd", 3, success},
78
+		{"abe", 4, success},
79
+		{"abf", 5, success},
80
+		{"abg", 6, success},
81
+		{"abh", 7, success},
82
+		{"abi", 8, success},
83
+		{"abj", 9, success},
84
+		{"abk", 0, success},
85
+		{"abl", 1, success},
86
+		{"abm", 2, success},
87
+		{"abn", 3, success},
88
+		{"abo", 4, success},
89
+		{"abp", 5, success},
90
+		{"abq", 6, success},
91
+		{"abr", 7, success},
92
+		{"abs", 8, success},
93
+		{"abt", 9, success},
94
+		{"abu", 0, success},
95
+		{"abv", 1, success},
96
+		{"abw", 2, success},
97
+		{"abx", 3, success},
98
+		{"aby", 4, success},
99
+		{"abz", 5, success},
100
+		{"aba", 0, failure},
101
+		{"abb", 1, failure},
102
+		{"abc", 2, failure},
103
+		{"abd", 3, failure},
104
+		{"abe", 4, failure},
105
+	}
106
+
107
+	for _, v := range data {
108
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
109
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
110
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
111
+		}
112
+	}
113
+}
114
+
115
+func TestTrie_DeleteDense(t *testing.T) {
116
+	trie := NewTrie()
117
+
118
+	data := []testData{
119
+		{"aba", 0, success},
120
+		{"abb", 1, success},
121
+		{"abc", 2, success},
122
+		{"abd", 3, success},
123
+		{"abe", 4, success},
124
+		{"abf", 5, success},
125
+		{"abg", 6, success},
126
+		{"abh", 7, success},
127
+		{"abi", 8, success},
128
+		{"abj", 9, success},
129
+		{"abk", 0, success},
130
+		{"abl", 1, success},
131
+		{"abm", 2, success},
132
+		{"abn", 3, success},
133
+		{"abo", 4, success},
134
+		{"abp", 5, success},
135
+		{"abq", 6, success},
136
+		{"abr", 7, success},
137
+		{"abs", 8, success},
138
+		{"abt", 9, success},
139
+		{"abu", 0, success},
140
+		{"abv", 1, success},
141
+		{"abw", 2, success},
142
+		{"abx", 3, success},
143
+		{"aby", 4, success},
144
+		{"abz", 5, success},
145
+	}
146
+
147
+	for _, v := range data {
148
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
149
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
150
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
151
+		}
152
+	}
153
+
154
+	for _, v := range data {
155
+		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
156
+		if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
157
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
158
+		}
159
+	}
160
+}
0 161
new file mode 100644
... ...
@@ -0,0 +1,659 @@
0
+// Copyright (c) 2014 The go-patricia AUTHORS
1
+//
2
+// Use of this source code is governed by The MIT License
3
+// that can be found in the LICENSE file.
4
+
5
+package patricia
6
+
7
+import (
8
+	"bytes"
9
+	"errors"
10
+	"fmt"
11
+	"strings"
12
+	"testing"
13
+)
14
+
15
+const (
16
+	success = true
17
+	failure = false
18
+)
19
+
20
+type testData struct {
21
+	key    string
22
+	value  interface{}
23
+	retVal bool
24
+}
25
+
26
+// Tests -----------------------------------------------------------------------
27
+
28
+func TestTrie_InsertDifferentPrefixes(t *testing.T) {
29
+	trie := NewTrie()
30
+
31
+	data := []testData{
32
+		{"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success},
33
+		{"Honzooooooooooooooo", "Honza Novak", success},
34
+		{"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success},
35
+	}
36
+
37
+	for _, v := range data {
38
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
39
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
40
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
41
+		}
42
+	}
43
+}
44
+
45
+func TestTrie_InsertDuplicatePrefixes(t *testing.T) {
46
+	trie := NewTrie()
47
+
48
+	data := []testData{
49
+		{"Pepan", "Pepan Zdepan", success},
50
+		{"Pepan", "Pepan Zdepan", failure},
51
+	}
52
+
53
+	for _, v := range data {
54
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
55
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
56
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
57
+		}
58
+	}
59
+}
60
+
61
+func TestTrie_InsertVariousPrefixes(t *testing.T) {
62
+	trie := NewTrie()
63
+
64
+	data := []testData{
65
+		{"Pepan", "Pepan Zdepan", success},
66
+		{"Pepin", "Pepin Omacka", success},
67
+		{"Honza", "Honza Novak", success},
68
+		{"Jenik", "Jenik Poustevnicek", success},
69
+		{"Pepan", "Pepan Dupan", failure},
70
+		{"Karel", "Karel Pekar", success},
71
+		{"Jenik", "Jenik Poustevnicek", failure},
72
+		{"Pepanek", "Pepanek Zemlicka", success},
73
+	}
74
+
75
+	for _, v := range data {
76
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
77
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
78
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
79
+		}
80
+	}
81
+}
82
+
83
+func TestTrie_InsertAndMatchPrefix(t *testing.T) {
84
+	trie := NewTrie()
85
+	t.Log("INSERT prefix=by week")
86
+	trie.Insert(Prefix("by week"), 2)
87
+	t.Log("INSERT prefix=by")
88
+	trie.Insert(Prefix("by"), 1)
89
+
90
+	if !trie.Match(Prefix("by")) {
91
+		t.Error("MATCH prefix=by, expected=true, got=false")
92
+	}
93
+}
94
+
95
+func TestTrie_SetGet(t *testing.T) {
96
+	trie := NewTrie()
97
+
98
+	data := []testData{
99
+		{"Pepan", "Pepan Zdepan", success},
100
+		{"Pepin", "Pepin Omacka", success},
101
+		{"Honza", "Honza Novak", success},
102
+		{"Jenik", "Jenik Poustevnicek", success},
103
+		{"Pepan", "Pepan Dupan", failure},
104
+		{"Karel", "Karel Pekar", success},
105
+		{"Jenik", "Jenik Poustevnicek", failure},
106
+		{"Pepanek", "Pepanek Zemlicka", success},
107
+	}
108
+
109
+	for _, v := range data {
110
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
111
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
112
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
113
+		}
114
+	}
115
+
116
+	for _, v := range data {
117
+		t.Logf("SET %q to 10", v.key)
118
+		trie.Set(Prefix(v.key), 10)
119
+	}
120
+
121
+	for _, v := range data {
122
+		value := trie.Get(Prefix(v.key))
123
+		t.Logf("GET %q => %v", v.key, value)
124
+		if value.(int) != 10 {
125
+			t.Errorf("Unexpected return value, != 10", value)
126
+		}
127
+	}
128
+
129
+	if value := trie.Get(Prefix("random crap")); value != nil {
130
+		t.Errorf("Unexpected return value, %v != <nil>", value)
131
+	}
132
+}
133
+
134
+func TestTrie_Match(t *testing.T) {
135
+	trie := NewTrie()
136
+
137
+	data := []testData{
138
+		{"Pepan", "Pepan Zdepan", success},
139
+		{"Pepin", "Pepin Omacka", success},
140
+		{"Honza", "Honza Novak", success},
141
+		{"Jenik", "Jenik Poustevnicek", success},
142
+		{"Pepan", "Pepan Dupan", failure},
143
+		{"Karel", "Karel Pekar", success},
144
+		{"Jenik", "Jenik Poustevnicek", failure},
145
+		{"Pepanek", "Pepanek Zemlicka", success},
146
+	}
147
+
148
+	for _, v := range data {
149
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
150
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
151
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
152
+		}
153
+	}
154
+
155
+	for _, v := range data {
156
+		matched := trie.Match(Prefix(v.key))
157
+		t.Logf("MATCH %q => %v", v.key, matched)
158
+		if !matched {
159
+			t.Errorf("Inserted key %q was not matched", v.key)
160
+		}
161
+	}
162
+
163
+	if trie.Match(Prefix("random crap")) {
164
+		t.Errorf("Key that was not inserted matched: %q", "random crap")
165
+	}
166
+}
167
+
168
+func TestTrie_MatchFalsePositive(t *testing.T) {
169
+	trie := NewTrie()
170
+
171
+	if ok := trie.Insert(Prefix("A"), 1); !ok {
172
+		t.Fatal("INSERT prefix=A, item=1 not ok")
173
+	}
174
+
175
+	resultMatchSubtree := trie.MatchSubtree(Prefix("A extra"))
176
+	resultMatch := trie.Match(Prefix("A extra"))
177
+
178
+	if resultMatchSubtree != false {
179
+		t.Error("MatchSubtree returned false positive")
180
+	}
181
+
182
+	if resultMatch != false {
183
+		t.Error("Match returned false positive")
184
+	}
185
+}
186
+
187
+func TestTrie_MatchSubtree(t *testing.T) {
188
+	trie := NewTrie()
189
+
190
+	data := []testData{
191
+		{"Pepan", "Pepan Zdepan", success},
192
+		{"Pepin", "Pepin Omacka", success},
193
+		{"Honza", "Honza Novak", success},
194
+		{"Jenik", "Jenik Poustevnicek", success},
195
+		{"Pepan", "Pepan Dupan", failure},
196
+		{"Karel", "Karel Pekar", success},
197
+		{"Jenik", "Jenik Poustevnicek", failure},
198
+		{"Pepanek", "Pepanek Zemlicka", success},
199
+	}
200
+
201
+	for _, v := range data {
202
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
203
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
204
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
205
+		}
206
+	}
207
+
208
+	for _, v := range data {
209
+		key := Prefix(v.key[:3])
210
+		matched := trie.MatchSubtree(key)
211
+		t.Logf("MATCH_SUBTREE %q => %v", key, matched)
212
+		if !matched {
213
+			t.Errorf("Subtree %q was not matched", v.key)
214
+		}
215
+	}
216
+}
217
+
218
+func TestTrie_Visit(t *testing.T) {
219
+	trie := NewTrie()
220
+
221
+	data := []testData{
222
+		{"Pepa", 0, success},
223
+		{"Pepa Zdepa", 1, success},
224
+		{"Pepa Kuchar", 2, success},
225
+		{"Honza", 3, success},
226
+		{"Jenik", 4, success},
227
+	}
228
+
229
+	for _, v := range data {
230
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
231
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
232
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
233
+		}
234
+	}
235
+
236
+	if err := trie.Visit(func(prefix Prefix, item Item) error {
237
+		name := data[item.(int)].key
238
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
239
+		if !strings.HasPrefix(string(prefix), name) {
240
+			t.Errorf("Unexpected prefix encountered, %q not a prefix of %q", prefix, name)
241
+		}
242
+		return nil
243
+	}); err != nil {
244
+		t.Fatal(err)
245
+	}
246
+}
247
+
248
+func TestTrie_VisitSkipSubtree(t *testing.T) {
249
+	trie := NewTrie()
250
+
251
+	data := []testData{
252
+		{"Pepa", 0, success},
253
+		{"Pepa Zdepa", 1, success},
254
+		{"Pepa Kuchar", 2, success},
255
+		{"Honza", 3, success},
256
+		{"Jenik", 4, success},
257
+	}
258
+
259
+	for _, v := range data {
260
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
261
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
262
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
263
+		}
264
+	}
265
+
266
+	if err := trie.Visit(func(prefix Prefix, item Item) error {
267
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
268
+		if item.(int) == 0 {
269
+			t.Logf("SKIP %q", prefix)
270
+			return SkipSubtree
271
+		}
272
+		if strings.HasPrefix(string(prefix), "Pepa") {
273
+			t.Errorf("Unexpected prefix encountered, %q", prefix)
274
+		}
275
+		return nil
276
+	}); err != nil {
277
+		t.Fatal(err)
278
+	}
279
+}
280
+
281
+func TestTrie_VisitReturnError(t *testing.T) {
282
+	trie := NewTrie()
283
+
284
+	data := []testData{
285
+		{"Pepa", 0, success},
286
+		{"Pepa Zdepa", 1, success},
287
+		{"Pepa Kuchar", 2, success},
288
+		{"Honza", 3, success},
289
+		{"Jenik", 4, success},
290
+	}
291
+
292
+	for _, v := range data {
293
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
294
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
295
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
296
+		}
297
+	}
298
+
299
+	someErr := errors.New("Something exploded")
300
+	if err := trie.Visit(func(prefix Prefix, item Item) error {
301
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
302
+		if item.(int) == 0 {
303
+			return someErr
304
+		}
305
+		if item.(int) != 0 {
306
+			t.Errorf("Unexpected prefix encountered, %q", prefix)
307
+		}
308
+		return nil
309
+	}); err != nil && err != someErr {
310
+		t.Fatal(err)
311
+	}
312
+}
313
+
314
+func TestTrie_VisitSubtree(t *testing.T) {
315
+	trie := NewTrie()
316
+
317
+	data := []testData{
318
+		{"Pepa", 0, success},
319
+		{"Pepa Zdepa", 1, success},
320
+		{"Pepa Kuchar", 2, success},
321
+		{"Honza", 3, success},
322
+		{"Jenik", 4, success},
323
+	}
324
+
325
+	for _, v := range data {
326
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
327
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
328
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
329
+		}
330
+	}
331
+
332
+	var counter int
333
+	subtreePrefix := []byte("Pep")
334
+	t.Log("VISIT Pep")
335
+	if err := trie.VisitSubtree(subtreePrefix, func(prefix Prefix, item Item) error {
336
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
337
+		if !bytes.HasPrefix(prefix, subtreePrefix) {
338
+			t.Errorf("Unexpected prefix encountered, %q does not extend %q",
339
+				prefix, subtreePrefix)
340
+		}
341
+		if len(prefix) > len(data[item.(int)].key) {
342
+			t.Fatalf("Something is rather fishy here, prefix=%q", prefix)
343
+		}
344
+		counter++
345
+		return nil
346
+	}); err != nil {
347
+		t.Fatal(err)
348
+	}
349
+
350
+	if counter != 3 {
351
+		t.Error("Unexpected number of nodes visited")
352
+	}
353
+}
354
+
355
+func TestTrie_VisitPrefixes(t *testing.T) {
356
+	trie := NewTrie()
357
+
358
+	data := []testData{
359
+		{"P", 0, success},
360
+		{"Pe", 1, success},
361
+		{"Pep", 2, success},
362
+		{"Pepa", 3, success},
363
+		{"Pepa Zdepa", 4, success},
364
+		{"Pepa Kuchar", 5, success},
365
+		{"Honza", 6, success},
366
+		{"Jenik", 7, success},
367
+	}
368
+
369
+	for _, v := range data {
370
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
371
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
372
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
373
+		}
374
+	}
375
+
376
+	var counter int
377
+	word := []byte("Pepa")
378
+	if err := trie.VisitPrefixes(word, func(prefix Prefix, item Item) error {
379
+		t.Logf("VISITING prefix=%q, item=%v", prefix, item)
380
+		if !bytes.HasPrefix(word, prefix) {
381
+			t.Errorf("Unexpected prefix encountered, %q is not a prefix of %q",
382
+				prefix, word)
383
+		}
384
+		counter++
385
+		return nil
386
+	}); err != nil {
387
+		t.Fatal(err)
388
+	}
389
+
390
+	if counter != 4 {
391
+		t.Error("Unexpected number of nodes visited")
392
+	}
393
+}
394
+
395
+func TestParticiaTrie_Delete(t *testing.T) {
396
+	trie := NewTrie()
397
+
398
+	data := []testData{
399
+		{"Pepan", "Pepan Zdepan", success},
400
+		{"Honza", "Honza Novak", success},
401
+		{"Jenik", "Jenik Poustevnicek", success},
402
+	}
403
+
404
+	for _, v := range data {
405
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
406
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
407
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
408
+		}
409
+	}
410
+
411
+	for _, v := range data {
412
+		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
413
+		if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
414
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
415
+		}
416
+	}
417
+}
418
+
419
+func TestParticiaTrie_DeleteNonExistent(t *testing.T) {
420
+	trie := NewTrie()
421
+
422
+	insertData := []testData{
423
+		{"Pepan", "Pepan Zdepan", success},
424
+		{"Honza", "Honza Novak", success},
425
+		{"Jenik", "Jenik Poustevnicek", success},
426
+	}
427
+	deleteData := []testData{
428
+		{"Pepan", "Pepan Zdepan", success},
429
+		{"Honza", "Honza Novak", success},
430
+		{"Pepan", "Pepan Zdepan", failure},
431
+		{"Jenik", "Jenik Poustevnicek", success},
432
+		{"Honza", "Honza Novak", failure},
433
+	}
434
+
435
+	for _, v := range insertData {
436
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
437
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
438
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
439
+		}
440
+	}
441
+
442
+	for _, v := range deleteData {
443
+		t.Logf("DELETE word=%v, success=%v", v.key, v.retVal)
444
+		if ok := trie.Delete([]byte(v.key)); ok != v.retVal {
445
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
446
+		}
447
+	}
448
+}
449
+
450
+func TestParticiaTrie_DeleteSubtree(t *testing.T) {
451
+	trie := NewTrie()
452
+
453
+	insertData := []testData{
454
+		{"P", 0, success},
455
+		{"Pe", 1, success},
456
+		{"Pep", 2, success},
457
+		{"Pepa", 3, success},
458
+		{"Pepa Zdepa", 4, success},
459
+		{"Pepa Kuchar", 5, success},
460
+		{"Honza", 6, success},
461
+		{"Jenik", 7, success},
462
+	}
463
+	deleteData := []testData{
464
+		{"Pe", -1, success},
465
+		{"Pe", -1, failure},
466
+		{"Honzik", -1, failure},
467
+		{"Honza", -1, success},
468
+		{"Honza", -1, failure},
469
+		{"Pep", -1, failure},
470
+		{"P", -1, success},
471
+		{"Nobody", -1, failure},
472
+		{"", -1, success},
473
+	}
474
+
475
+	for _, v := range insertData {
476
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
477
+		if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
478
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
479
+		}
480
+	}
481
+
482
+	for _, v := range deleteData {
483
+		t.Logf("DELETE_SUBTREE prefix=%v, success=%v", v.key, v.retVal)
484
+		if ok := trie.DeleteSubtree([]byte(v.key)); ok != v.retVal {
485
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
486
+		}
487
+	}
488
+}
489
+
490
+/*
491
+func TestTrie_Dump(t *testing.T) {
492
+	trie := NewTrie()
493
+
494
+	data := []testData{
495
+		{"Honda", nil, success},
496
+		{"Honza", nil, success},
497
+		{"Jenik", nil, success},
498
+		{"Pepan", nil, success},
499
+		{"Pepin", nil, success},
500
+	}
501
+
502
+	for i, v := range data {
503
+		if _, ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal {
504
+			t.Logf("INSERT %v %v", v.key, v.value)
505
+			t.Fatalf("Unexpected return value, expected=%v, got=%v", i, ok)
506
+		}
507
+	}
508
+
509
+	dump := `
510
++--+--+ Hon +--+--+ da
511
+   |           |
512
+   |           +--+ za
513
+   |
514
+   +--+ Jenik
515
+   |
516
+   +--+ Pep +--+--+ an
517
+               |
518
+               +--+ in
519
+`
520
+
521
+	var buf bytes.Buffer
522
+	trie.Dump(buf)
523
+
524
+	if !bytes.Equal(buf.Bytes(), dump) {
525
+		t.Logf("DUMP")
526
+		t.Fatalf("Unexpected dump generated, expected\n\n%v\ngot\n\n%v", dump, buf.String())
527
+	}
528
+}
529
+*/
530
+
531
+func TestTrie_compact(t *testing.T) {
532
+	trie := NewTrie()
533
+
534
+	trie.Insert(Prefix("a"), 0)
535
+	trie.Insert(Prefix("ab"), 0)
536
+	trie.Insert(Prefix("abc"), 0)
537
+	trie.Insert(Prefix("abcd"), 0)
538
+	trie.Insert(Prefix("abcde"), 0)
539
+	trie.Insert(Prefix("abcdef"), 0)
540
+	trie.Insert(Prefix("abcdefg"), 0)
541
+	trie.Insert(Prefix("abcdefgi"), 0)
542
+	trie.Insert(Prefix("abcdefgij"), 0)
543
+	trie.Insert(Prefix("abcdefgijk"), 0)
544
+
545
+	trie.Delete(Prefix("abcdef"))
546
+	trie.Delete(Prefix("abcde"))
547
+	trie.Delete(Prefix("abcdefg"))
548
+
549
+	trie.Delete(Prefix("a"))
550
+	trie.Delete(Prefix("abc"))
551
+	trie.Delete(Prefix("ab"))
552
+
553
+	trie.Visit(func(prefix Prefix, item Item) error {
554
+		// 97 ~~ 'a',
555
+		for ch := byte(97); ch <= 107; ch++ {
556
+			if c := bytes.Count(prefix, []byte{ch}); c > 1 {
557
+				t.Errorf("%q appeared in %q %v times", ch, prefix, c)
558
+			}
559
+		}
560
+		return nil
561
+	})
562
+}
563
+
564
+func TestTrie_longestCommonPrefixLenght(t *testing.T) {
565
+	trie := NewTrie()
566
+	trie.prefix = []byte("1234567890")
567
+
568
+	switch {
569
+	case trie.longestCommonPrefixLength([]byte("")) != 0:
570
+		t.Fail()
571
+	case trie.longestCommonPrefixLength([]byte("12345")) != 5:
572
+		t.Fail()
573
+	case trie.longestCommonPrefixLength([]byte("123789")) != 3:
574
+		t.Fail()
575
+	case trie.longestCommonPrefixLength([]byte("12345678901")) != 10:
576
+		t.Fail()
577
+	}
578
+}
579
+
580
+// Examples --------------------------------------------------------------------
581
+
582
+func ExampleTrie() {
583
+	// Create a new tree.
584
+	trie := NewTrie()
585
+
586
+	// Insert some items.
587
+	trie.Insert(Prefix("Pepa Novak"), 1)
588
+	trie.Insert(Prefix("Pepa Sindelar"), 2)
589
+	trie.Insert(Prefix("Karel Macha"), 3)
590
+	trie.Insert(Prefix("Karel Hynek Macha"), 4)
591
+
592
+	// Just check if some things are present in the tree.
593
+	key := Prefix("Pepa Novak")
594
+	fmt.Printf("%q present? %v\n", key, trie.Match(key))
595
+	key = Prefix("Karel")
596
+	fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
597
+
598
+	// Walk the tree.
599
+	trie.Visit(printItem)
600
+	// "Pepa Novak": 1
601
+	// "Pepa Sindelar": 2
602
+	// "Karel Macha": 3
603
+	// "Karel Hynek Macha": 4
604
+
605
+	// Walk a subtree.
606
+	trie.VisitSubtree(Prefix("Pepa"), printItem)
607
+	// "Pepa Novak": 1
608
+	// "Pepa Sindelar": 2
609
+
610
+	// Modify an item, then fetch it from the tree.
611
+	trie.Set(Prefix("Karel Hynek Macha"), 10)
612
+	key = Prefix("Karel Hynek Macha")
613
+	fmt.Printf("%q: %v\n", key, trie.Get(key))
614
+	// "Karel Hynek Macha": 10
615
+
616
+	// Walk prefixes.
617
+	prefix := Prefix("Karel Hynek Macha je kouzelnik")
618
+	trie.VisitPrefixes(prefix, printItem)
619
+	// "Karel Hynek Macha": 10
620
+
621
+	// Delete some items.
622
+	trie.Delete(Prefix("Pepa Novak"))
623
+	trie.Delete(Prefix("Karel Macha"))
624
+
625
+	// Walk again.
626
+	trie.Visit(printItem)
627
+	// "Pepa Sindelar": 2
628
+	// "Karel Hynek Macha": 10
629
+
630
+	// Delete a subtree.
631
+	trie.DeleteSubtree(Prefix("Pepa"))
632
+
633
+	// Print what is left.
634
+	trie.Visit(printItem)
635
+	// "Karel Hynek Macha": 10
636
+
637
+	// Output:
638
+	// "Pepa Novak" present? true
639
+	// Anybody called "Karel" here? true
640
+	// "Pepa Novak": 1
641
+	// "Pepa Sindelar": 2
642
+	// "Karel Macha": 3
643
+	// "Karel Hynek Macha": 4
644
+	// "Pepa Novak": 1
645
+	// "Pepa Sindelar": 2
646
+	// "Karel Hynek Macha": 10
647
+	// "Karel Hynek Macha": 10
648
+	// "Pepa Sindelar": 2
649
+	// "Karel Hynek Macha": 10
650
+	// "Karel Hynek Macha": 10
651
+}
652
+
653
+// Helpers ---------------------------------------------------------------------
654
+
655
+func printItem(prefix Prefix, item Item) error {
656
+	fmt.Printf("%q: %v\n", prefix, item)
657
+	return nil
658
+}
0 659
new file mode 100644
... ...
@@ -0,0 +1,78 @@
0
+// Copyright (c) 2014 The go-patricia AUTHORS
1
+//
2
+// Use of this source code is governed by The MIT License
3
+// that can be found in the LICENSE file.
4
+
5
+package patricia
6
+
7
+import (
8
+	"crypto/rand"
9
+	"reflect"
10
+	"testing"
11
+)
12
+
13
+// Tests -----------------------------------------------------------------------
14
+
15
+func TestTrie_GetNonexistentPrefix(t *testing.T) {
16
+	trie := NewTrie()
17
+
18
+	data := []testData{
19
+		{"aba", 0, success},
20
+	}
21
+
22
+	for _, v := range data {
23
+		t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal)
24
+		if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal {
25
+			t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok)
26
+		}
27
+	}
28
+
29
+	t.Logf("GET prefix=baa, expect item=nil")
30
+	if item := trie.Get(Prefix("baa")); item != nil {
31
+		t.Errorf("Unexpected return value, expected=<nil>, got=%v", item)
32
+	}
33
+}
34
+
35
+func TestTrie_RandomKitchenSink(t *testing.T) {
36
+	if testing.Short() {
37
+		t.Skip()
38
+	}
39
+	const count, size = 750000, 16
40
+	b := make([]byte, count+size+1)
41
+	if _, err := rand.Read(b); err != nil {
42
+		t.Fatal("error generating random bytes", err)
43
+	}
44
+	m := make(map[string]string)
45
+	for i := 0; i < count; i++ {
46
+		m[string(b[i:i+size])] = string(b[i+1 : i+size+1])
47
+	}
48
+	trie := NewTrie()
49
+	getAndDelete := func(k, v string) {
50
+		i := trie.Get(Prefix(k))
51
+		if i == nil {
52
+			t.Fatalf("item not found, prefix=%v", []byte(k))
53
+		} else if s, ok := i.(string); !ok {
54
+			t.Fatalf("unexpected item type, expecting=%v, got=%v", reflect.TypeOf(k), reflect.TypeOf(i))
55
+		} else if s != v {
56
+			t.Fatalf("unexpected item, expecting=%v, got=%v", []byte(k), []byte(s))
57
+		} else if !trie.Delete(Prefix(k)) {
58
+			t.Fatalf("delete failed, prefix=%v", []byte(k))
59
+		} else if i = trie.Get(Prefix(k)); i != nil {
60
+			t.Fatalf("unexpected item, expecting=<nil>, got=%v", i)
61
+		} else if trie.Delete(Prefix(k)) {
62
+			t.Fatalf("extra delete succeeded, prefix=%v", []byte(k))
63
+		}
64
+	}
65
+	for k, v := range m {
66
+		if !trie.Insert(Prefix(k), v) {
67
+			t.Fatalf("insert failed, prefix=%v", []byte(k))
68
+		}
69
+		if byte(k[size/2]) < 128 {
70
+			getAndDelete(k, v)
71
+			delete(m, k)
72
+		}
73
+	}
74
+	for k, v := range m {
75
+		getAndDelete(k, v)
76
+	}
77
+}