├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md └── patricia ├── children.go ├── patricia.go ├── patricia_dense_test.go ├── patricia_sparse_test.go └── patricia_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Swap files. 2 | *.swp 3 | 4 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 5 | *.o 6 | *.a 7 | *.so 8 | 9 | # Folders 10 | _obj 11 | _test 12 | 13 | # Architecture specific extensions/prefixes 14 | *.[568vq] 15 | [568vq].out 16 | 17 | *.cgo1.go 18 | *.cgo2.c 19 | _cgo_defun.c 20 | _cgo_gotypes.go 21 | _cgo_export.* 22 | 23 | _testmain.go 24 | 25 | *.exe 26 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This is the complete list of go-patricia copyright holders: 2 | 3 | Ondřej Kupka 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 The AUTHORS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fuzzy-patricia # 2 | 3 | **Documentation**: [GoDoc](http://godoc.org/github.com/ozeidan/fuzzy-patricia/patricia)
4 | 5 | ## About ## 6 | A generic patricia trie (also called radix tree) implemented in Go. 7 | 8 | This is a fork of [go-patricia](https://github.com/tchap/go-patricia), 9 | which is optimized for memory consumption and also has slightly better 10 | performance. It additionally provides the functionality for fuzzy and substring 11 | searching on the inserted keys. 12 | 13 | The patricia trie as implemented in this package enables fast visiting of items 14 | in the following ways: 15 | 16 | 1. Visit all items saved in the tree, 17 | 2. Visit all items matching particular prefix (visit subtree). 18 | 3. Visit items by fuzzy matching a query to the nodes keys. 19 | 3. Visit items by searching for a query inside the nodes keys. 20 | 3. Given a string, visit all items matching some prefix of that string. 21 | 22 | `[]byte` type is used for keys, `interface{}` for values. 23 | `Trie` is not thread safe. Synchronize the access yourself. 24 | 25 | ## Usage ## 26 | 27 | Import the package from GitHub first. 28 | 29 | ```go 30 | import "github.com/ozeidan/fuzzy-patricia/patricia" 31 | ``` 32 | 33 | You can also import from gopkg.in, recommended when using go modules: 34 | 35 | ```go 36 | import "gopkg.in/ozeidan/fuzzy-patricia.v3/patricia" 37 | ``` 38 | 39 | Then you can start having fun. 40 | 41 | ```go 42 | printItem := func(prefix patricia.Prefix, item patricia.Item) error { 43 | fmt.Printf("%q: %v\n", prefix, item) 44 | return nil 45 | } 46 | 47 | // Create a new default trie (using the default parameter values). 48 | trie := NewTrie() 49 | 50 | // Create a new custom trie. 51 | trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10)) 52 | 53 | // Insert some items. 54 | trie.Insert(Prefix("Pepa Novak"), 1) 55 | trie.Insert(Prefix("Pepa Sindelar"), 2) 56 | trie.Insert(Prefix("Karel Macha"), 3) 57 | trie.Insert(Prefix("Karel Hynek Macha"), 4) 58 | 59 | // Just check if some things are present in the tree. 60 | key := Prefix("Pepa Novak") 61 | fmt.Printf("%q present? %v\n", key, trie.Match(key)) 62 | // "Pepa Novak" present? true 63 | key = Prefix("Karel") 64 | fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key)) 65 | // Anybody called "Karel" here? true 66 | 67 | // Walk the tree in alphabetical order. 68 | trie.Visit(printItem) 69 | // "Karel Hynek Macha": 4 70 | // "Karel Macha": 3 71 | // "Pepa Novak": 1 72 | // "Pepa Sindelar": 2 73 | 74 | // Walk a subtree. 75 | trie.VisitSubtree(Prefix("Pepa"), printItem) 76 | // "Pepa Novak": 1 77 | // "Pepa Sindelar": 2 78 | 79 | // Modify an item, then fetch it from the tree. 80 | trie.Set(Prefix("Karel Hynek Macha"), 10) 81 | key = Prefix("Karel Hynek Macha") 82 | fmt.Printf("%q: %v\n", key, trie.Get(key)) 83 | // "Karel Hynek Macha": 10 84 | 85 | // Walk prefixes. 86 | prefix := Prefix("Karel Hynek Macha je kouzelnik") 87 | trie.VisitPrefixes(prefix, printItem) 88 | // "Karel Hynek Macha": 10 89 | 90 | // Delete some items. 91 | trie.Delete(Prefix("Pepa Novak")) 92 | trie.Delete(Prefix("Karel Macha")) 93 | 94 | // Walk again. 95 | trie.Visit(printItem) 96 | // "Karel Hynek Macha": 10 97 | // "Pepa Sindelar": 2 98 | 99 | // Delete a subtree. 100 | trie.DeleteSubtree(Prefix("Pepa")) 101 | 102 | // Print what is left. 103 | trie.Visit(printItem) 104 | // "Karel Hynek Macha": 10 105 | ``` 106 | 107 | ## License ## 108 | MIT, check the `LICENSE` file. 109 | -------------------------------------------------------------------------------- /patricia/children.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The go-patricia AUTHORS 2 | // 3 | // Use of this source code is governed by The MIT License 4 | // that can be found in the LICENSE file. 5 | 6 | package patricia 7 | 8 | import ( 9 | "io" 10 | "sort" 11 | ) 12 | 13 | type childList interface { 14 | length() int 15 | head() *Trie 16 | add(child *Trie) childList 17 | remove(b byte) 18 | replace(b byte, child *Trie) 19 | next(b byte) *Trie 20 | combinedMask() uint64 21 | getChildren() []*Trie 22 | walk(prefix *Prefix, visitor VisitorFunc) error 23 | print(w io.Writer, indent int) 24 | clone() childList 25 | total() int 26 | } 27 | 28 | type tries []*Trie 29 | 30 | func (t tries) Len() int { 31 | return len(t) 32 | } 33 | 34 | func (t tries) Less(i, j int) bool { 35 | strings := sort.StringSlice{string(t[i].prefix), string(t[j].prefix)} 36 | return strings.Less(0, 1) 37 | } 38 | 39 | func (t tries) Swap(i, j int) { 40 | t[i], t[j] = t[j], t[i] 41 | } 42 | 43 | type childContainer struct { 44 | char byte 45 | node *Trie 46 | } 47 | type superDenseChildList struct { 48 | children []childContainer 49 | } 50 | 51 | func newSuperDenseChildList() childList { 52 | return &superDenseChildList{ 53 | make([]childContainer, 0), 54 | } 55 | } 56 | 57 | func (list *superDenseChildList) length() int { 58 | return len(list.children) 59 | } 60 | 61 | func (list *superDenseChildList) head() *Trie { 62 | if len(list.children) > 0 { 63 | return list.children[0].node 64 | } 65 | return nil 66 | } 67 | 68 | func (list *superDenseChildList) add(child *Trie) childList { 69 | char := child.prefix[0] 70 | list.children = append(list.children, childContainer{ 71 | char, 72 | child, 73 | }) 74 | return list 75 | } 76 | 77 | func (list *superDenseChildList) remove(b byte) { 78 | children := list.children 79 | for i := 0; i < len(children); i++ { 80 | if children[i].char == b { 81 | // children[i] = children[len(children)-1] 82 | // children = children[:len(children)-1] 83 | // children = append(children[:i], children[i+1:]...) 84 | newChildren := make([]childContainer, len(children)-1) 85 | // copy the elements over to avoid "memory leaks" 86 | copy(newChildren, children[:i]) 87 | copy(newChildren[i:], children[i+1:]) 88 | list.children = newChildren 89 | 90 | // list.children = make([]childContainer, len(children)) 91 | // copy(list.children, children) 92 | // children = nil 93 | 94 | return 95 | } 96 | } 97 | } 98 | 99 | func (list *superDenseChildList) replace(b byte, child *Trie) { 100 | children := list.children 101 | for i := 0; i < len(list.children); i++ { 102 | if children[i].char == b { 103 | children[i].node = child 104 | return 105 | } 106 | } 107 | } 108 | 109 | func (list *superDenseChildList) next(b byte) *Trie { 110 | children := list.children 111 | for i := 0; i < len(list.children); i++ { 112 | if children[i].char == b { 113 | return children[i].node 114 | } 115 | } 116 | return nil 117 | } 118 | 119 | func (list superDenseChildList) combinedMask() uint64 { 120 | var mask uint64 121 | for _, child := range list.children { 122 | // fmt.Printf("child = %+v\n", child) 123 | mask |= child.node.mask 124 | } 125 | return mask 126 | } 127 | 128 | func (list *superDenseChildList) getChildren() []*Trie { 129 | children := make([]*Trie, 0, len(list.children)) 130 | for _, child := range list.children { 131 | children = append(children, child.node) 132 | } 133 | return children 134 | } 135 | 136 | func (list *superDenseChildList) walk(prefix *Prefix, visitor VisitorFunc) error { 137 | for _, child := range list.children { 138 | node := child.node 139 | *prefix = append(*prefix, node.prefix...) 140 | if node.item != nil { 141 | if err := visitor(*prefix, node.item); err != nil { 142 | if err == SkipSubtree { 143 | *prefix = (*prefix)[:len(*prefix)-len(node.prefix)] 144 | continue 145 | } 146 | *prefix = (*prefix)[:len(*prefix)-len(node.prefix)] 147 | return err 148 | } 149 | } 150 | 151 | err := node.children.walk(prefix, visitor) 152 | *prefix = (*prefix)[:len(*prefix)-len(node.prefix)] 153 | if err != nil { 154 | return err 155 | } 156 | } 157 | 158 | return nil 159 | } 160 | 161 | func (list *superDenseChildList) print(w io.Writer, indent int) { 162 | for _, child := range list.children { 163 | child.node.print(w, indent) 164 | } 165 | } 166 | 167 | func (list *superDenseChildList) clone() childList { 168 | clones := make([]childContainer, len(list.children)) 169 | 170 | for i := 0; i < len(list.children); i++ { 171 | child := list.children[i] 172 | clones[i] = childContainer{child.char, child.node.Clone()} 173 | } 174 | 175 | return &superDenseChildList{ 176 | clones, 177 | } 178 | } 179 | 180 | func (list *superDenseChildList) total() int { 181 | return len(list.children) 182 | } 183 | -------------------------------------------------------------------------------- /patricia/patricia.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The go-patricia AUTHORS 2 | // 3 | // Use of this source code is governed by The MIT License 4 | // that can be found in the LICENSE file. 5 | 6 | package patricia 7 | 8 | import ( 9 | "bytes" 10 | "errors" 11 | "fmt" 12 | "io" 13 | "strings" 14 | ) 15 | 16 | //------------------------------------------------------------------------------ 17 | // Trie 18 | //------------------------------------------------------------------------------ 19 | 20 | const ( 21 | defaultMaxPrefixPerNode = 10 22 | ) 23 | 24 | var ( 25 | maxPrefixPerNode = defaultMaxPrefixPerNode 26 | ) 27 | 28 | type ( 29 | // Prefix is the type of node prefixes 30 | Prefix []byte 31 | // Item is just interface{} 32 | Item interface{} 33 | // VisitorFunc is the type of functions passed to visit function 34 | VisitorFunc func(prefix Prefix, item Item) error 35 | // FuzzyVisitorFunc additionaly returns how many characters were skipped which can be sorted on 36 | FuzzyVisitorFunc func(prefix Prefix, item Item, skipped int) error 37 | ) 38 | 39 | // Trie is a generic patricia trie that allows fast retrieval of items by prefix. 40 | // and other funky stuff. 41 | // 42 | // Trie is not thread-safe. 43 | type Trie struct { 44 | prefix Prefix 45 | item Item 46 | mask uint64 47 | 48 | children childList 49 | } 50 | 51 | // Public API ------------------------------------------------------------------ 52 | 53 | // NewTrie constructs a new trie. 54 | func NewTrie() *Trie { 55 | trie := &Trie{} 56 | 57 | trie.children = newSuperDenseChildList() 58 | 59 | trie.mask = 0 60 | return trie 61 | } 62 | 63 | // SetMaxPrefixPerNode sets the maximum length of a prefix before it is split into two nodes 64 | func SetMaxPrefixPerNode(value int) { 65 | maxPrefixPerNode = value 66 | } 67 | 68 | // Clone makes a copy of an existing trie. 69 | // Items stored in both tries become shared, obviously. 70 | func (trie *Trie) Clone() *Trie { 71 | return &Trie{ 72 | prefix: append(Prefix(nil), trie.prefix...), 73 | item: trie.item, 74 | children: trie.children.clone(), 75 | } 76 | } 77 | 78 | // Item returns the item stored in the root of this trie. 79 | func (trie *Trie) Item() Item { 80 | return trie.item 81 | } 82 | 83 | // Insert inserts a new item into the trie using the given prefix. Insert does 84 | // not replace existing items. It returns false if an item was already in place. 85 | func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) { 86 | return trie.put(key, item, false) 87 | } 88 | 89 | // Set works much like Insert, but it always sets the item, possibly replacing 90 | // the item previously inserted. 91 | func (trie *Trie) Set(key Prefix, item Item) { 92 | trie.put(key, item, true) 93 | } 94 | 95 | // Get returns the item located at key. 96 | // 97 | // This method is a bit dangerous, because Get can as well end up in an internal 98 | // node that is not really representing any user-defined value. So when nil is 99 | // a valid value being used, it is not possible to tell if the value was inserted 100 | // into the tree by the user or not. A possible workaround for this is not to use 101 | // nil interface as a valid value, even using zero value of any type is enough 102 | // to prevent this bad behaviour. 103 | func (trie *Trie) Get(key Prefix) (item Item) { 104 | _, node, found, leftover := trie.findSubtree(key) 105 | if !found || len(leftover) != 0 { 106 | return nil 107 | } 108 | return node.item 109 | } 110 | 111 | // Match returns what Get(prefix) != nil would return. The same warning as for 112 | // Get applies here as well. 113 | func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) { 114 | return trie.Get(prefix) != nil 115 | } 116 | 117 | // MatchSubtree returns true when there is a subtree representing extensions 118 | // to key, that is if there are any keys in the tree which have key as prefix. 119 | func (trie *Trie) MatchSubtree(key Prefix) (matched bool) { 120 | _, _, matched, _ = trie.findSubtree(key) 121 | return 122 | } 123 | 124 | // Visit calls visitor on every node containing a non-nil item 125 | // in alphabetical order. 126 | // 127 | // If an error is returned from visitor, the function stops visiting the tree 128 | // and returns that error, unless it is a special error - SkipSubtree. In that 129 | // case Visit skips the subtree represented by the current node and continues 130 | // elsewhere. 131 | func (trie *Trie) Visit(visitor VisitorFunc) error { 132 | return trie.walk(nil, visitor) 133 | } 134 | 135 | func (trie *Trie) size() int { 136 | n := 0 137 | 138 | err := trie.walk(nil, func(prefix Prefix, item Item) error { 139 | n++ 140 | return nil 141 | }) 142 | 143 | if err != nil { 144 | panic(err) 145 | } 146 | 147 | return n 148 | } 149 | 150 | func (trie *Trie) total() int { 151 | return 1 + trie.children.total() 152 | } 153 | 154 | // VisitSubtree works much like Visit, but it only visits nodes matching prefix. 155 | func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error { 156 | // Nil prefix not allowed. 157 | if prefix == nil { 158 | panic(ErrNilPrefix) 159 | } 160 | 161 | // Empty trie must be handled explicitly. 162 | if trie.prefix == nil { 163 | return nil 164 | } 165 | 166 | // Locate the relevant subtree. 167 | _, root, found, leftover := trie.findSubtree(prefix) 168 | if !found { 169 | return nil 170 | } 171 | prefix = append(prefix, leftover...) 172 | 173 | // Visit it. 174 | return root.walk(prefix, visitor) 175 | } 176 | 177 | type potentialSubtree struct { 178 | idx int 179 | skipped int 180 | prefix Prefix 181 | node *Trie 182 | } 183 | 184 | // VisitFuzzy visits every node that is succesfully matched via fuzzy matching 185 | func (trie *Trie) VisitFuzzy(partial Prefix, caseInsensitive bool, visitor FuzzyVisitorFunc) error { 186 | if len(partial) == 0 { 187 | return trie.VisitPrefixes(partial, caseInsensitive, func(prefix Prefix, item Item) error { 188 | return visitor(prefix, item, 0) 189 | }) 190 | } 191 | 192 | var ( 193 | m uint64 194 | cmp uint64 195 | i int 196 | p potentialSubtree 197 | ) 198 | 199 | potential := []potentialSubtree{potentialSubtree{node: trie, prefix: Prefix(""), idx: 0}} 200 | for l := len(potential); l > 0; l = len(potential) { 201 | i = l - 1 202 | p = potential[i] 203 | 204 | potential = potential[:i] 205 | m = makePrefixMask(partial[p.idx:]) 206 | 207 | if caseInsensitive { 208 | cmp = caseInsensitiveMask(p.node.mask) 209 | } else { 210 | cmp = p.node.mask 211 | } 212 | 213 | if (cmp & m) != m { 214 | continue 215 | } 216 | 217 | matchCount, skipped := fuzzyMatchCount(p.node.prefix, 218 | partial[p.idx:], p.idx, caseInsensitive) 219 | p.idx += matchCount 220 | if p.idx != 0 { 221 | p.skipped += skipped 222 | } 223 | 224 | if p.idx == len(partial) { 225 | fullPrefix := append(p.prefix, p.node.prefix...) 226 | 227 | err := p.node.walk(Prefix(""), func(prefix Prefix, item Item) error { 228 | key := make([]byte, len(fullPrefix), len(fullPrefix)+len(prefix)) 229 | copy(key, fullPrefix) 230 | key = append(key, prefix...) 231 | 232 | err := visitor(key, item, p.skipped) 233 | if err != nil { 234 | return err 235 | } 236 | 237 | return nil 238 | }) 239 | if err != nil { 240 | return err 241 | } 242 | 243 | continue 244 | } 245 | 246 | for _, c := range p.node.children.getChildren() { 247 | if c != nil { 248 | newPrefix := make(Prefix, len(p.prefix), len(p.prefix)+len(p.node.prefix)) 249 | copy(newPrefix, p.prefix) 250 | newPrefix = append(newPrefix, p.node.prefix...) 251 | potential = append(potential, potentialSubtree{ 252 | node: c, 253 | prefix: newPrefix, 254 | idx: p.idx, 255 | skipped: p.skipped, 256 | }) 257 | } else { 258 | fmt.Println("warning, child isn il") 259 | } 260 | } 261 | } 262 | 263 | return nil 264 | } 265 | 266 | func fuzzyMatchCount(prefix, query Prefix, idx int, caseInsensitive bool) (count, skipped int) { 267 | for i := 0; i < len(prefix); i++ { 268 | var match bool 269 | 270 | if caseInsensitive { 271 | match = matchCaseInsensitive(prefix[i], query[count]) 272 | } else { 273 | match = prefix[i] == query[count] 274 | } 275 | 276 | if !match { 277 | if count+idx > 0 { 278 | skipped++ 279 | } 280 | continue 281 | } 282 | 283 | count++ 284 | if count >= len(query) { 285 | return 286 | } 287 | } 288 | return 289 | } 290 | 291 | // VisitSubstring takes a substring and visits all the nodes that whos prefix contains this substring 292 | func (trie *Trie) VisitSubstring(substring Prefix, caseInsensitive bool, visitor VisitorFunc) error { 293 | if len(substring) == 0 { 294 | return trie.VisitSubtree(substring, visitor) 295 | } 296 | 297 | var ( 298 | m uint64 299 | cmp uint64 300 | i int 301 | p potentialSubtree 302 | suffixLen int 303 | maxSuffixLen = len(substring) - 1 304 | ) 305 | 306 | potential := []potentialSubtree{potentialSubtree{node: trie, prefix: nil}} 307 | for l := len(potential); l > 0; l = len(potential) { 308 | i = l - 1 309 | p = potential[i] 310 | 311 | potential = potential[:i] 312 | 313 | if len(p.prefix) < maxSuffixLen { 314 | suffixLen = len(p.prefix) 315 | } else { 316 | suffixLen = maxSuffixLen 317 | } 318 | 319 | searchBytes := append(p.prefix[len(p.prefix)-suffixLen:], p.node.prefix...) 320 | 321 | contains := false 322 | 323 | if caseInsensitive { 324 | contains = bytes.Contains(bytes.ToUpper(searchBytes), bytes.ToUpper(substring)) 325 | } else { 326 | contains = bytes.Contains(searchBytes, substring) 327 | } 328 | 329 | if contains { 330 | fullPrefix := append(p.prefix, p.node.prefix...) 331 | err := p.node.walk(Prefix(""), func(prefix Prefix, item Item) error { 332 | key := make([]byte, len(fullPrefix), len(fullPrefix)+len(prefix)) 333 | copy(key, fullPrefix) 334 | key = append(key, prefix...) 335 | copy(key, append(fullPrefix, prefix...)) 336 | 337 | err := visitor(key, item) 338 | if err != nil { 339 | return err 340 | } 341 | 342 | return nil 343 | }) 344 | if err != nil { 345 | return err 346 | } 347 | } 348 | 349 | newPrefix := make(Prefix, len(p.prefix), len(p.prefix)+len(p.node.prefix)) 350 | copy(newPrefix, p.prefix) 351 | newPrefix = append(newPrefix, p.node.prefix...) 352 | 353 | overLap := overlapLength(newPrefix, substring, caseInsensitive) 354 | m = makePrefixMask(substring[overLap:]) 355 | 356 | for _, c := range p.node.children.getChildren() { 357 | if caseInsensitive { 358 | cmp = caseInsensitiveMask(c.mask) 359 | } else { 360 | cmp = c.mask 361 | } 362 | if c != nil && (cmp&m == m) { 363 | potential = append(potential, potentialSubtree{ 364 | node: c, 365 | prefix: newPrefix, 366 | }) 367 | } 368 | } 369 | } 370 | 371 | return nil 372 | } 373 | 374 | func overlapLength(prefix, query Prefix, caseInsensitive bool) int { 375 | startLength := len(query) - 1 376 | if len(prefix) < startLength { 377 | startLength = len(prefix) 378 | } 379 | for i := startLength; i > 0; i-- { 380 | suffix := prefix[len(prefix)-i:] 381 | queryPrefix := query[:i] 382 | if caseInsensitive { 383 | if bytes.EqualFold(suffix, queryPrefix) { 384 | return i 385 | } 386 | } else if bytes.Equal(suffix, queryPrefix) { 387 | return i 388 | } 389 | } 390 | 391 | return 0 392 | } 393 | 394 | // VisitPrefixes visits only nodes that represent prefixes of key. 395 | // To say the obvious, returning SkipSubtree from visitor makes no sense here. 396 | func (trie *Trie) VisitPrefixes(key Prefix, caseInsensitive bool, visitor VisitorFunc) error { 397 | // Nil key not allowed. 398 | if key == nil { 399 | panic(ErrNilPrefix) 400 | } 401 | 402 | // Empty trie must be handled explicitly. 403 | if trie.prefix == nil { 404 | return nil 405 | } 406 | 407 | // Walk the path matching key prefixes. 408 | node := trie 409 | prefix := key 410 | offset := 0 411 | for { 412 | // Compute what part of prefix matches. 413 | common := node.longestCommonPrefixLength(key, caseInsensitive) 414 | key = key[common:] 415 | offset += common 416 | 417 | // Partial match means that there is no subtree matching prefix. 418 | if common < len(node.prefix) { 419 | return nil 420 | } 421 | 422 | // Call the visitor. 423 | if item := node.item; item != nil { 424 | if err := visitor(prefix[:offset], item); err != nil { 425 | return err 426 | } 427 | } 428 | 429 | if len(key) == 0 { 430 | // This node represents key, we are finished. 431 | return nil 432 | } 433 | 434 | // There is some key suffix left, move to the children. 435 | child := node.children.next(key[0]) 436 | if child == nil { 437 | // There is nowhere to continue, return. 438 | return nil 439 | } 440 | 441 | node = child 442 | } 443 | } 444 | 445 | // Delete deletes the item represented by the given prefix. 446 | // 447 | // True is returned if the matching node was found and deleted. 448 | func (trie *Trie) Delete(key Prefix) (deleted bool) { 449 | // Nil prefix not allowed. 450 | if key == nil { 451 | panic(ErrNilPrefix) 452 | } 453 | 454 | // Empty trie must be handled explicitly. 455 | if trie.prefix == nil { 456 | return false 457 | } 458 | 459 | // Find the relevant node. 460 | path, found, _ := trie.findSubtreePath(key) 461 | if !found { 462 | return false 463 | } 464 | 465 | node := path[len(path)-1] 466 | var parent *Trie 467 | if len(path) != 1 { 468 | parent = path[len(path)-2] 469 | } 470 | 471 | // If the item is already set to nil, there is nothing to do. 472 | if node.item == nil { 473 | return false 474 | } 475 | 476 | // Delete the item. 477 | node.item = nil 478 | 479 | // Initialise i before goto. 480 | // Will be used later in a loop. 481 | i := len(path) - 1 482 | 483 | // In case there are some child nodes, we cannot drop the whole subtree. 484 | // We can try to compact nodes, though. 485 | if node.children.length() != 0 { 486 | goto Compact 487 | } 488 | 489 | // In case we are at the root, just reset it and we are done. 490 | if parent == nil { 491 | node.reset() 492 | return true 493 | } 494 | 495 | // We can drop a subtree. 496 | // Find the first ancestor that has its value set or it has 2 or more child nodes. 497 | // That will be the node where to drop the subtree at. 498 | for ; i >= 0; i-- { 499 | if current := path[i]; current.item != nil || current.children.length() >= 2 { 500 | break 501 | } 502 | } 503 | 504 | // Handle the case when there is no such node. 505 | // In other words, we can reset the whole tree. 506 | if i == -1 { 507 | path[0].reset() 508 | return true 509 | } 510 | 511 | // We can just remove the subtree here. 512 | node = path[i] 513 | if i == 0 { 514 | parent = nil 515 | } else { 516 | parent = path[i-1] 517 | } 518 | // i+1 is always a valid index since i is never pointing to the last node. 519 | // The loop above skips at least the last node since we are sure that the item 520 | // is set to nil and it has no children, othewise we would be compacting instead. 521 | node.children.remove(path[i+1].prefix[0]) 522 | 523 | // lastly, the bitmasks of all of the parent nodes have to be updated again, since 524 | // a child node of all of them has bin removed 525 | for ; i >= 0; i-- { 526 | n := path[i] 527 | n.mask = n.children.combinedMask() 528 | } 529 | 530 | Compact: 531 | // The node is set to the first non-empty ancestor, 532 | // so try to compact since that might be possible now. 533 | if compacted := node.compact(); compacted != node { 534 | if parent == nil { 535 | *node = *compacted 536 | } else { 537 | parent.children.replace(node.prefix[0], compacted) 538 | *parent = *parent.compact() 539 | } 540 | } 541 | 542 | return true 543 | } 544 | 545 | // DeleteSubtree finds the subtree exactly matching prefix and deletes it. 546 | // 547 | // True is returned if the subtree was found and deleted. 548 | func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) { 549 | // Nil prefix not allowed. 550 | if prefix == nil { 551 | panic(ErrNilPrefix) 552 | } 553 | 554 | // Empty trie must be handled explicitly. 555 | if trie.prefix == nil { 556 | return false 557 | } 558 | 559 | // Locate the relevant subtree. 560 | parent, root, found, _ := trie.findSubtree(prefix) 561 | path, _, _ := trie.findSubtreePath(prefix) 562 | if !found { 563 | return false 564 | } 565 | 566 | // If we are in the root of the trie, reset the trie. 567 | if parent == nil { 568 | root.reset() 569 | return true 570 | } 571 | 572 | // Otherwise remove the root node from its parent. 573 | parent.children.remove(root.prefix[0]) 574 | 575 | // update masks 576 | parent.mask = parent.children.combinedMask() 577 | for i := len(path) - 1; i >= 0; i-- { 578 | n := path[i] 579 | n.mask = n.children.combinedMask() 580 | } 581 | 582 | return true 583 | } 584 | 585 | // Internal helper methods ----------------------------------------------------- 586 | 587 | func (trie *Trie) empty() bool { 588 | return trie.item == nil && trie.children.length() == 0 589 | } 590 | 591 | func (trie *Trie) reset() { 592 | trie.prefix = nil 593 | trie.children = newSuperDenseChildList() 594 | } 595 | 596 | func makePrefixMask(key Prefix) uint64 { 597 | var mask uint64 598 | for _, b := range key { 599 | if b >= '0' && b <= '9' { 600 | // 0-9 bits: 0-9 601 | b -= 48 602 | } else if b >= 'A' && b <= 'Z' { 603 | // A-Z bits: 10-35 604 | b -= 55 605 | } else if b >= 'a' && b <= 'z' { 606 | // a-z bits: 36-61 607 | b -= 61 608 | } else if b == '.' { 609 | b = 62 610 | } else if b == '-' { 611 | b = 63 612 | } else { 613 | continue 614 | } 615 | mask |= uint64(1) << uint64(b) 616 | } 617 | return mask 618 | } 619 | 620 | const upperBits = 0xFFFFFFC00 621 | const lowerBits = 0x3FFFFFF000000000 622 | 623 | func caseInsensitiveMask(mask uint64) uint64 { 624 | mask |= (mask & upperBits) << uint64(26) 625 | mask |= (mask & lowerBits) >> uint64(26) 626 | return mask 627 | } 628 | 629 | var charmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-" 630 | 631 | func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) { 632 | // Nil prefix not allowed. 633 | if key == nil { 634 | panic(ErrNilPrefix) 635 | } 636 | 637 | var ( 638 | common int 639 | node = trie 640 | child *Trie 641 | mask uint64 642 | ) 643 | 644 | mask = makePrefixMask(key) 645 | 646 | if node.prefix == nil { 647 | node.mask |= mask 648 | if len(key) <= maxPrefixPerNode { 649 | node.prefix = key 650 | goto InsertItem 651 | } 652 | node.prefix = key[:maxPrefixPerNode] 653 | key = key[maxPrefixPerNode:] 654 | mask = makePrefixMask(key) 655 | goto AppendChild 656 | } 657 | 658 | for { 659 | // Compute the longest common prefix length. 660 | common = node.longestCommonPrefixLength(key, false) 661 | key = key[common:] 662 | 663 | // Only a part matches, split. 664 | if common < len(node.prefix) { 665 | goto SplitPrefix 666 | } 667 | 668 | // common == len(node.prefix) since never (common > len(node.prefix)) 669 | // common == len(former key) <-> 0 == len(key) 670 | // -> former key == node.prefix 671 | if len(key) == 0 { 672 | goto InsertItem 673 | } 674 | 675 | node.mask |= mask 676 | // Check children for matching prefix. 677 | child = node.children.next(key[0]) 678 | if child == nil { 679 | goto AppendChild 680 | } 681 | node = child 682 | } 683 | 684 | SplitPrefix: 685 | // Split the prefix if necessary. 686 | child = new(Trie) 687 | *child = *node 688 | *node = *NewTrie() 689 | node.prefix = child.prefix[:common] 690 | child.prefix = child.prefix[common:] 691 | child = child.compact() 692 | node.children = node.children.add(child) 693 | node.mask = child.mask 694 | node.mask |= mask 695 | mask = makePrefixMask(key) 696 | 697 | AppendChild: 698 | // Keep appending children until whole prefix is inserted. 699 | // This loop starts with empty node.prefix that needs to be filled. 700 | for len(key) != 0 { 701 | child := NewTrie() 702 | child.mask = mask 703 | if len(key) <= maxPrefixPerNode { 704 | child.prefix = key 705 | node.children = node.children.add(child) 706 | node = child 707 | goto InsertItem 708 | } else { 709 | child.prefix = key[:maxPrefixPerNode] 710 | key = key[maxPrefixPerNode:] 711 | mask = makePrefixMask(key) 712 | node.children = node.children.add(child) 713 | node = child 714 | } 715 | } 716 | 717 | InsertItem: 718 | // Try to insert the item if possible. 719 | if replace || node.item == nil { 720 | node.item = item 721 | return true 722 | } 723 | return false 724 | } 725 | 726 | func (trie *Trie) compact() *Trie { 727 | // Only a node with a single child can be compacted. 728 | if trie.children.length() != 1 { 729 | return trie 730 | } 731 | 732 | child := trie.children.head() 733 | 734 | // If any item is set, we cannot compact since we want to retain 735 | // the ability to do searching by key. This makes compaction less usable, 736 | // but that simply cannot be avoided. 737 | if trie.item != nil || child.item != nil { 738 | return trie 739 | } 740 | 741 | // Make sure the combined prefixes fit into a single node. 742 | if len(trie.prefix)+len(child.prefix) > maxPrefixPerNode { 743 | return trie 744 | } 745 | 746 | // Concatenate the prefixes, move the items. 747 | child.prefix = append(trie.prefix, child.prefix...) 748 | child.mask = trie.mask 749 | if trie.item != nil { 750 | child.item = trie.item 751 | } 752 | 753 | return child 754 | } 755 | 756 | func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) { 757 | // Find the subtree matching prefix. 758 | root = trie 759 | for { 760 | // Compute what part of prefix matches. 761 | common := root.longestCommonPrefixLength(prefix, false) 762 | prefix = prefix[common:] 763 | 764 | // We used up the whole prefix, subtree found. 765 | if len(prefix) == 0 { 766 | found = true 767 | leftover = root.prefix[common:] 768 | return 769 | } 770 | 771 | // Partial match means that there is no subtree matching prefix. 772 | if common < len(root.prefix) { 773 | leftover = root.prefix[common:] 774 | return 775 | } 776 | 777 | // There is some prefix left, move to the children. 778 | child := root.children.next(prefix[0]) 779 | if child == nil { 780 | // There is nowhere to continue, there is no subtree matching prefix. 781 | return 782 | } 783 | 784 | parent = root 785 | root = child 786 | } 787 | } 788 | 789 | func (trie *Trie) findSubtreePath(prefix Prefix) (path []*Trie, found bool, leftover Prefix) { 790 | // Find the subtree matching prefix. 791 | root := trie 792 | var subtreePath []*Trie 793 | for { 794 | // Append the current root to the path. 795 | subtreePath = append(subtreePath, root) 796 | 797 | // Compute what part of prefix matches. 798 | common := root.longestCommonPrefixLength(prefix, false) 799 | prefix = prefix[common:] 800 | 801 | // We used up the whole prefix, subtree found. 802 | if len(prefix) == 0 { 803 | path = subtreePath 804 | found = true 805 | leftover = root.prefix[common:] 806 | return 807 | } 808 | 809 | // Partial match means that there is no subtree matching prefix. 810 | if common < len(root.prefix) { 811 | leftover = root.prefix[common:] 812 | return 813 | } 814 | 815 | // There is some prefix left, move to the children. 816 | child := root.children.next(prefix[0]) 817 | if child == nil { 818 | // There is nowhere to continue, there is no subtree matching prefix. 819 | return 820 | } 821 | 822 | root = child 823 | } 824 | } 825 | 826 | func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error { 827 | var prefix Prefix 828 | // Allocate a bit more space for prefix at the beginning. 829 | if actualRootPrefix == nil { 830 | prefix = make(Prefix, 32+len(trie.prefix)) 831 | copy(prefix, trie.prefix) 832 | prefix = prefix[:len(trie.prefix)] 833 | } else { 834 | prefix = make(Prefix, 32+len(actualRootPrefix)) 835 | copy(prefix, actualRootPrefix) 836 | prefix = prefix[:len(actualRootPrefix)] 837 | } 838 | 839 | // Visit the root first. Not that this works for empty trie as well since 840 | // in that case item == nil && len(children) == 0. 841 | if trie.item != nil { 842 | if err := visitor(prefix, trie.item); err != nil { 843 | if err == SkipSubtree { 844 | return nil 845 | } 846 | return err 847 | } 848 | } 849 | 850 | // Then continue to the children. 851 | return trie.children.walk(&prefix, visitor) 852 | } 853 | 854 | func (trie *Trie) longestCommonPrefixLength(prefix Prefix, caseInsensitive bool) (i int) { 855 | for ; i < len(prefix) && i < len(trie.prefix); i++ { 856 | p := prefix[i] 857 | t := trie.prefix[i] 858 | 859 | if caseInsensitive { 860 | if !(matchCaseInsensitive(t, p)) { 861 | break 862 | } 863 | } else { 864 | if p != t { 865 | break 866 | } 867 | } 868 | } 869 | return 870 | } 871 | 872 | func matchCaseInsensitive(a byte, b byte) bool { 873 | return a == b+32 || b == a+32 || a == b 874 | } 875 | 876 | func (trie *Trie) dump() string { 877 | writer := &bytes.Buffer{} 878 | trie.print(writer, 0) 879 | return writer.String() 880 | } 881 | 882 | func (trie *Trie) print(writer io.Writer, indent int) { 883 | fmt.Fprintf(writer, "%s%s %v\n", strings.Repeat(" ", indent), string(trie.prefix), trie.item) 884 | trie.children.print(writer, indent+2) 885 | } 886 | 887 | // Errors ---------------------------------------------------------------------- 888 | 889 | var ( 890 | SkipSubtree = errors.New("Skip this subtree") 891 | ErrNilPrefix = errors.New("Nil prefix passed into a method call") 892 | ) 893 | -------------------------------------------------------------------------------- /patricia/patricia_dense_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The go-patricia AUTHORS 2 | // 3 | // Use of this source code is governed by The MIT License 4 | // that can be found in the LICENSE file. 5 | 6 | package patricia 7 | 8 | import ( 9 | "math/rand" 10 | "runtime" 11 | "strconv" 12 | "testing" 13 | ) 14 | 15 | // Tests ----------------------------------------------------------------------- 16 | 17 | // overhead is allowed tolerance for Go's runtime/GC to increase the allocated memory 18 | // (to avoid failing tests on insignificant growth amounts) 19 | const overhead = 4000 20 | 21 | func TestTrie_InsertDense(t *testing.T) { 22 | trie := NewTrie() 23 | 24 | data := []testData{ 25 | {"aba", 0, success}, 26 | {"abb", 1, success}, 27 | {"abc", 2, success}, 28 | {"abd", 3, success}, 29 | {"abe", 4, success}, 30 | {"abf", 5, success}, 31 | {"abg", 6, success}, 32 | {"abh", 7, success}, 33 | {"abi", 8, success}, 34 | {"abj", 9, success}, 35 | {"abk", 0, success}, 36 | {"abl", 1, success}, 37 | {"abm", 2, success}, 38 | {"abn", 3, success}, 39 | {"abo", 4, success}, 40 | {"abp", 5, success}, 41 | {"abq", 6, success}, 42 | {"abr", 7, success}, 43 | {"abs", 8, success}, 44 | {"abt", 9, success}, 45 | {"abu", 0, success}, 46 | {"abv", 1, success}, 47 | {"abw", 2, success}, 48 | {"abx", 3, success}, 49 | {"aby", 4, success}, 50 | {"abz", 5, success}, 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_InsertDenseDuplicatePrefixes(t *testing.T) { 62 | trie := NewTrie() 63 | 64 | data := []testData{ 65 | {"aba", 0, success}, 66 | {"abb", 1, success}, 67 | {"abc", 2, success}, 68 | {"abd", 3, success}, 69 | {"abe", 4, success}, 70 | {"abf", 5, success}, 71 | {"abg", 6, success}, 72 | {"abh", 7, success}, 73 | {"abi", 8, success}, 74 | {"abj", 9, success}, 75 | {"abk", 0, success}, 76 | {"abl", 1, success}, 77 | {"abm", 2, success}, 78 | {"abn", 3, success}, 79 | {"abo", 4, success}, 80 | {"abp", 5, success}, 81 | {"abq", 6, success}, 82 | {"abr", 7, success}, 83 | {"abs", 8, success}, 84 | {"abt", 9, success}, 85 | {"abu", 0, success}, 86 | {"abv", 1, success}, 87 | {"abw", 2, success}, 88 | {"abx", 3, success}, 89 | {"aby", 4, success}, 90 | {"abz", 5, success}, 91 | {"aba", 0, failure}, 92 | {"abb", 1, failure}, 93 | {"abc", 2, failure}, 94 | {"abd", 3, failure}, 95 | {"abe", 4, failure}, 96 | } 97 | 98 | for _, v := range data { 99 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 100 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 101 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 102 | } 103 | } 104 | } 105 | 106 | func TestTrie_CloneDense(t *testing.T) { 107 | trie := NewTrie() 108 | 109 | data := []testData{ 110 | {"aba", 0, success}, 111 | {"abb", 1, success}, 112 | {"abc", 2, success}, 113 | {"abd", 3, success}, 114 | {"abe", 4, success}, 115 | {"abf", 5, success}, 116 | {"abg", 6, success}, 117 | {"abh", 7, success}, 118 | {"abi", 8, success}, 119 | {"abj", 9, success}, 120 | {"abk", 0, success}, 121 | {"abl", 1, success}, 122 | {"abm", 2, success}, 123 | {"abn", 3, success}, 124 | {"abo", 4, success}, 125 | {"abp", 5, success}, 126 | {"abq", 6, success}, 127 | {"abr", 7, success}, 128 | {"abs", 8, success}, 129 | {"abt", 9, success}, 130 | {"abu", 0, success}, 131 | {"abv", 1, success}, 132 | {"abw", 2, success}, 133 | {"abx", 3, success}, 134 | {"aby", 4, success}, 135 | {"abz", 5, success}, 136 | } 137 | 138 | for _, v := range data { 139 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 140 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 141 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 142 | } 143 | } 144 | 145 | t.Log("CLONE") 146 | clone := trie.Clone() 147 | 148 | for _, v := range data { 149 | t.Logf("GET prefix=%v, item=%v", v.key, v.value) 150 | if item := clone.Get(Prefix(v.key)); item != v.value { 151 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.value, item) 152 | } 153 | } 154 | 155 | prefix := "xxx" 156 | item := 666 157 | t.Logf("INSERT prefix=%v, item=%v", prefix, item) 158 | if ok := trie.Insert(Prefix(prefix), item); !ok { 159 | t.Errorf("Unexpected return value, expected=true, got=%v", ok) 160 | } 161 | t.Logf("GET cloned prefix=%v", prefix) 162 | if item := clone.Get(Prefix(prefix)); item != nil { 163 | t.Errorf("Unexpected return value, expected=nil, got=%v", item) 164 | } 165 | } 166 | 167 | func TestTrie_DeleteDense(t *testing.T) { 168 | trie := NewTrie() 169 | 170 | data := []testData{ 171 | {"aba", 0, success}, 172 | {"abb", 1, success}, 173 | {"abc", 2, success}, 174 | {"abd", 3, success}, 175 | {"abe", 4, success}, 176 | {"abf", 5, success}, 177 | {"abg", 6, success}, 178 | {"abh", 7, success}, 179 | {"abi", 8, success}, 180 | {"abj", 9, success}, 181 | {"abk", 0, success}, 182 | {"abl", 1, success}, 183 | {"abm", 2, success}, 184 | {"abn", 3, success}, 185 | {"abo", 4, success}, 186 | {"abp", 5, success}, 187 | {"abq", 6, success}, 188 | {"abr", 7, success}, 189 | {"abs", 8, success}, 190 | {"abt", 9, success}, 191 | {"abu", 0, success}, 192 | {"abv", 1, success}, 193 | {"abw", 2, success}, 194 | {"abx", 3, success}, 195 | {"aby", 4, success}, 196 | {"abz", 5, success}, 197 | } 198 | 199 | for _, v := range data { 200 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 201 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 202 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 203 | } 204 | } 205 | 206 | for _, v := range data { 207 | t.Logf("DELETE word=%v, success=%v", v.key, v.retVal) 208 | if ok := trie.Delete([]byte(v.key)); ok != v.retVal { 209 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 210 | } 211 | } 212 | } 213 | 214 | func TestTrie_DeleteLeakageDense(t *testing.T) { 215 | trie := NewTrie() 216 | 217 | genTestData := func() *testData { 218 | // Generate a random integer as a key. 219 | key := strconv.FormatUint(rand.Uint64(), 10) 220 | return &testData{key: key, value: "v", retVal: success} 221 | } 222 | 223 | testSize := 100 224 | data := make([]*testData, 0, testSize) 225 | for i := 0; i < testSize; i++ { 226 | data = append(data, genTestData()) 227 | } 228 | 229 | oldBytes := heapAllocatedBytes() 230 | 231 | // repeat insertion/deletion for 10K times to catch possible memory issues 232 | for i := 0; i < 10000; i++ { 233 | for _, v := range data { 234 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 235 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 236 | } 237 | } 238 | 239 | for _, v := range data { 240 | if ok := trie.Delete([]byte(v.key)); ok != v.retVal { 241 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 242 | } 243 | } 244 | } 245 | 246 | if newBytes := heapAllocatedBytes(); newBytes > oldBytes+overhead { 247 | t.Logf("Size=%d, Total=%d, Trie state:\n%s\n", trie.size(), trie.total(), trie.dump()) 248 | t.Errorf("Heap space leak, grew %d bytes (%d to %d)\n", newBytes-oldBytes, oldBytes, newBytes) 249 | } 250 | 251 | if numChildren := trie.children.length(); numChildren != 0 { 252 | t.Errorf("Trie is not empty: %v children found", numChildren) 253 | } 254 | } 255 | 256 | func heapAllocatedBytes() uint64 { 257 | runtime.GC() 258 | 259 | ms := runtime.MemStats{} 260 | runtime.ReadMemStats(&ms) 261 | return ms.Alloc 262 | } 263 | -------------------------------------------------------------------------------- /patricia/patricia_sparse_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The go-patricia AUTHORS 2 | // 3 | // Use of this source code is governed by The MIT License 4 | // that can be found in the LICENSE file. 5 | 6 | package patricia 7 | 8 | import ( 9 | "bytes" 10 | "errors" 11 | "fmt" 12 | "strings" 13 | "testing" 14 | ) 15 | 16 | const ( 17 | success = true 18 | failure = false 19 | ) 20 | 21 | type testData struct { 22 | key string 23 | value interface{} 24 | retVal bool 25 | } 26 | 27 | // Tests ----------------------------------------------------------------------- 28 | 29 | func TestTrie_InsertDifferentPrefixes(t *testing.T) { 30 | trie := NewTrie() 31 | 32 | data := []testData{ 33 | {"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success}, 34 | {"Honzooooooooooooooo", "Honza Novak", success}, 35 | {"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success}, 36 | } 37 | 38 | for _, v := range data { 39 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 40 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 41 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 42 | } 43 | } 44 | } 45 | 46 | func TestTrie_InsertDuplicatePrefixes(t *testing.T) { 47 | trie := NewTrie() 48 | 49 | data := []testData{ 50 | {"Pepan", "Pepan Zdepan", success}, 51 | {"Pepan", "Pepan Zdepan", failure}, 52 | } 53 | 54 | for _, v := range data { 55 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 56 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 57 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 58 | } 59 | } 60 | } 61 | 62 | func TestTrie_InsertVariousPrefixes(t *testing.T) { 63 | trie := NewTrie() 64 | 65 | data := []testData{ 66 | {"Pepan", "Pepan Zdepan", success}, 67 | {"Pepin", "Pepin Omacka", success}, 68 | {"Honza", "Honza Novak", success}, 69 | {"Jenik", "Jenik Poustevnicek", success}, 70 | {"Pepan", "Pepan Dupan", failure}, 71 | {"Karel", "Karel Pekar", success}, 72 | {"Jenik", "Jenik Poustevnicek", failure}, 73 | {"Pepanek", "Pepanek Zemlicka", success}, 74 | } 75 | 76 | for _, v := range data { 77 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 78 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 79 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 80 | } 81 | } 82 | } 83 | 84 | func TestTrie_InsertAndMatchPrefix(t *testing.T) { 85 | trie := NewTrie() 86 | t.Log("INSERT prefix=by week") 87 | trie.Insert(Prefix("by week"), 2) 88 | t.Log("INSERT prefix=by") 89 | trie.Insert(Prefix("by"), 1) 90 | 91 | if !trie.Match(Prefix("by")) { 92 | t.Error("MATCH prefix=by, expected=true, got=false") 93 | } 94 | } 95 | 96 | func TestTrie_SetGet(t *testing.T) { 97 | trie := NewTrie() 98 | 99 | data := []testData{ 100 | {"Pepan", "Pepan Zdepan", success}, 101 | {"Pepin", "Pepin Omacka", success}, 102 | {"Honza", "Honza Novak", success}, 103 | {"Jenik", "Jenik Poustevnicek", success}, 104 | {"Pepan", "Pepan Dupan", failure}, 105 | {"Karel", "Karel Pekar", success}, 106 | {"Jenik", "Jenik Poustevnicek", failure}, 107 | {"Pepanek", "Pepanek Zemlicka", success}, 108 | } 109 | 110 | for _, v := range data { 111 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 112 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 113 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 114 | } 115 | } 116 | 117 | for _, v := range data { 118 | t.Logf("SET %q to 10", v.key) 119 | trie.Set(Prefix(v.key), 10) 120 | } 121 | 122 | for _, v := range data { 123 | value := trie.Get(Prefix(v.key)) 124 | t.Logf("GET %q => %v", v.key, value) 125 | if value.(int) != 10 { 126 | t.Errorf("Unexpected return value, %v != 10", value) 127 | } 128 | } 129 | 130 | if value := trie.Get(Prefix("random crap")); value != nil { 131 | t.Errorf("Unexpected return value, %v != ", value) 132 | } 133 | } 134 | 135 | func TestTrie_Match(t *testing.T) { 136 | trie := NewTrie() 137 | 138 | data := []testData{ 139 | {"Pepan", "Pepan Zdepan", success}, 140 | {"Pepin", "Pepin Omacka", success}, 141 | {"Honza", "Honza Novak", success}, 142 | {"Jenik", "Jenik Poustevnicek", success}, 143 | {"Pepan", "Pepan Dupan", failure}, 144 | {"Karel", "Karel Pekar", success}, 145 | {"Jenik", "Jenik Poustevnicek", failure}, 146 | {"Pepanek", "Pepanek Zemlicka", success}, 147 | } 148 | 149 | for _, v := range data { 150 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 151 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 152 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 153 | } 154 | } 155 | 156 | for _, v := range data { 157 | matched := trie.Match(Prefix(v.key)) 158 | t.Logf("MATCH %q => %v", v.key, matched) 159 | if !matched { 160 | t.Errorf("Inserted key %q was not matched", v.key) 161 | } 162 | } 163 | 164 | if trie.Match(Prefix("random crap")) { 165 | t.Errorf("Key that was not inserted matched: %q", "random crap") 166 | } 167 | } 168 | 169 | func TestTrie_MatchFalsePositive(t *testing.T) { 170 | trie := NewTrie() 171 | 172 | if ok := trie.Insert(Prefix("A"), 1); !ok { 173 | t.Fatal("INSERT prefix=A, item=1 not ok") 174 | } 175 | 176 | resultMatchSubtree := trie.MatchSubtree(Prefix("A extra")) 177 | resultMatch := trie.Match(Prefix("A extra")) 178 | 179 | if resultMatchSubtree != false { 180 | t.Error("MatchSubtree returned false positive") 181 | } 182 | 183 | if resultMatch != false { 184 | t.Error("Match returned false positive") 185 | } 186 | } 187 | 188 | func TestTrie_MatchSubtree(t *testing.T) { 189 | trie := NewTrie() 190 | 191 | data := []testData{ 192 | {"Pepan", "Pepan Zdepan", success}, 193 | {"Pepin", "Pepin Omacka", success}, 194 | {"Honza", "Honza Novak", success}, 195 | {"Jenik", "Jenik Poustevnicek", success}, 196 | {"Pepan", "Pepan Dupan", failure}, 197 | {"Karel", "Karel Pekar", success}, 198 | {"Jenik", "Jenik Poustevnicek", failure}, 199 | {"Pepanek", "Pepanek Zemlicka", success}, 200 | } 201 | 202 | for _, v := range data { 203 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 204 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 205 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 206 | } 207 | } 208 | 209 | for _, v := range data { 210 | key := Prefix(v.key[:3]) 211 | matched := trie.MatchSubtree(key) 212 | t.Logf("MATCH_SUBTREE %q => %v", key, matched) 213 | if !matched { 214 | t.Errorf("Subtree %q was not matched", v.key) 215 | } 216 | } 217 | } 218 | 219 | func TestTrie_Visit(t *testing.T) { 220 | trie := NewTrie() 221 | 222 | data := []testData{ 223 | {"Pepa", 0, success}, 224 | {"Pepa Zdepa", 1, success}, 225 | {"Pepa Kuchar", 2, success}, 226 | {"Honza", 3, success}, 227 | {"Jenik", 4, success}, 228 | } 229 | 230 | for _, v := range data { 231 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 232 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 233 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 234 | } 235 | } 236 | 237 | if err := trie.Visit(func(prefix Prefix, item Item) error { 238 | name := data[item.(int)].key 239 | t.Logf("VISITING prefix=%q, item=%v", prefix, item) 240 | if !strings.HasPrefix(string(prefix), name) { 241 | t.Errorf("Unexpected prefix encountered, %q not a prefix of %q", prefix, name) 242 | } 243 | return nil 244 | }); err != nil { 245 | t.Fatal(err) 246 | } 247 | } 248 | 249 | func TestTrie_VisitSkipSubtree(t *testing.T) { 250 | trie := NewTrie() 251 | 252 | data := []testData{ 253 | {"Pepa", 0, success}, 254 | {"Pepa Zdepa", 1, success}, 255 | {"Pepa Kuchar", 2, success}, 256 | {"Honza", 3, success}, 257 | {"Jenik", 4, success}, 258 | } 259 | 260 | for _, v := range data { 261 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 262 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 263 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 264 | } 265 | } 266 | 267 | if err := trie.Visit(func(prefix Prefix, item Item) error { 268 | t.Logf("VISITING prefix=%q, item=%v", prefix, item) 269 | if item.(int) == 0 { 270 | t.Logf("SKIP %q", prefix) 271 | return SkipSubtree 272 | } 273 | if strings.HasPrefix(string(prefix), "Pepa") { 274 | t.Errorf("Unexpected prefix encountered, %q", prefix) 275 | } 276 | return nil 277 | }); err != nil { 278 | t.Fatal(err) 279 | } 280 | } 281 | 282 | func TestTrie_VisitReturnError(t *testing.T) { 283 | trie := NewTrie() 284 | 285 | data := []testData{ 286 | {"Pepa", 0, success}, 287 | {"Pepa Zdepa", 1, success}, 288 | {"Pepa Kuchar", 2, success}, 289 | {"Honza", 3, success}, 290 | {"Jenik", 4, success}, 291 | } 292 | 293 | for _, v := range data { 294 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 295 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 296 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 297 | } 298 | } 299 | 300 | someErr := errors.New("Something exploded") 301 | if err := trie.Visit(func(prefix Prefix, item Item) error { 302 | t.Logf("VISITING prefix=%q, item=%v", prefix, item) 303 | if item.(int) == 3 { 304 | return someErr 305 | } 306 | if item.(int) != 3 { 307 | t.Logf("Unexpected prefix encountered, %q", prefix) 308 | } 309 | return nil 310 | }); err != nil && err != someErr { 311 | t.Fatal(err) 312 | } 313 | } 314 | 315 | func TestTrie_VisitSubtree(t *testing.T) { 316 | trie := NewTrie() 317 | 318 | data := []testData{ 319 | {"Pepa", 0, success}, 320 | {"Pepa Zdepa", 1, success}, 321 | {"Pepa Kuchar", 2, success}, 322 | {"Honza", 3, success}, 323 | {"Jenik", 4, success}, 324 | } 325 | 326 | for _, v := range data { 327 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 328 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 329 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 330 | } 331 | } 332 | 333 | var counter int 334 | subtreePrefix := []byte("Pep") 335 | t.Log("VISIT Pep") 336 | if err := trie.VisitSubtree(subtreePrefix, func(prefix Prefix, item Item) error { 337 | t.Logf("VISITING prefix=%q, item=%v", prefix, item) 338 | if !bytes.HasPrefix(prefix, subtreePrefix) { 339 | t.Errorf("Unexpected prefix encountered, %q does not extend %q", 340 | prefix, subtreePrefix) 341 | } 342 | if len(prefix) > len(data[item.(int)].key) { 343 | t.Fatalf("Something is rather fishy here, prefix=%q", prefix) 344 | } 345 | counter++ 346 | return nil 347 | }); err != nil { 348 | t.Fatal(err) 349 | } 350 | 351 | if counter != 3 { 352 | t.Error("Unexpected number of nodes visited") 353 | } 354 | } 355 | 356 | func TestTrie_VisitPrefixes(t *testing.T) { 357 | trie := NewTrie() 358 | 359 | data := []testData{ 360 | {"P", 0, success}, 361 | {"Pe", 1, success}, 362 | {"Pep", 2, success}, 363 | {"Pepa", 3, success}, 364 | {"Pepa Zdepa", 4, success}, 365 | {"Pepa Kuchar", 5, success}, 366 | {"Honza", 6, success}, 367 | {"Jenik", 7, success}, 368 | } 369 | 370 | for _, v := range data { 371 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 372 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 373 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 374 | } 375 | } 376 | 377 | var counter int 378 | word := []byte("Pepa") 379 | if err := trie.VisitPrefixes(word, false, func(prefix Prefix, item Item) error { 380 | t.Logf("VISITING prefix=%q, item=%v", prefix, item) 381 | if !bytes.HasPrefix(word, prefix) { 382 | t.Errorf("Unexpected prefix encountered, %q is not a prefix of %q", 383 | prefix, word) 384 | } 385 | counter++ 386 | return nil 387 | }); err != nil { 388 | t.Fatal(err) 389 | } 390 | 391 | if counter != 4 { 392 | t.Error("Unexpected number of nodes visited") 393 | } 394 | } 395 | 396 | func TestPatriciaTrie_CloneSparse(t *testing.T) { 397 | trie := NewTrie() 398 | 399 | data := []testData{ 400 | {"Pepaneeeeeeeeeeeeee", "Pepan Zdepan", success}, 401 | {"Honzooooooooooooooo", "Honza Novak", success}, 402 | {"Jenikuuuuuuuuuuuuuu", "Jenik Poustevnicek", success}, 403 | } 404 | 405 | for _, v := range data { 406 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 407 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 408 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 409 | } 410 | } 411 | 412 | t.Log("CLONE") 413 | clone := trie.Clone() 414 | 415 | for _, v := range data { 416 | t.Logf("GET prefix=%v, item=%v", v.key, v.value) 417 | if item := clone.Get(Prefix(v.key)); item != v.value { 418 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.value, item) 419 | } 420 | } 421 | 422 | prefix := "xxx" 423 | item := 666 424 | t.Logf("INSERT prefix=%v, item=%v", prefix, item) 425 | if ok := trie.Insert(Prefix(prefix), item); !ok { 426 | t.Errorf("Unexpected return value, expected=true, got=%v", ok) 427 | } 428 | t.Logf("GET cloned prefix=%v", prefix) 429 | if item := clone.Get(Prefix(prefix)); item != nil { 430 | t.Errorf("Unexpected return value, expected=nil, got=%v", item) 431 | } 432 | } 433 | 434 | func TestParticiaTrie_Delete(t *testing.T) { 435 | trie := NewTrie() 436 | 437 | data := []testData{ 438 | {"Pepan", "Pepan Zdepan", success}, 439 | {"Honza", "Honza Novak", success}, 440 | {"Jenik", "Jenik Poustevnicek", success}, 441 | } 442 | 443 | for _, v := range data { 444 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 445 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 446 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 447 | } 448 | } 449 | 450 | for _, v := range data { 451 | t.Logf("DELETE word=%v, success=%v", v.key, v.retVal) 452 | if ok := trie.Delete([]byte(v.key)); ok != v.retVal { 453 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 454 | } 455 | } 456 | } 457 | 458 | func TestParticiaTrie_DeleteLeakageSparse(t *testing.T) { 459 | trie := NewTrie() 460 | 461 | data := []testData{ 462 | {"Pepan", "Pepan Zdepan", success}, 463 | {"Honza", "Honza Novak", success}, 464 | {"Jenik", "Jenik Poustevnicek", success}, 465 | } 466 | 467 | oldBytes := heapAllocatedBytes() 468 | 469 | for i := 0; i < 10000; i++ { 470 | for _, v := range data { 471 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 472 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 473 | } 474 | } 475 | 476 | for _, v := range data { 477 | if ok := trie.Delete([]byte(v.key)); ok != v.retVal { 478 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 479 | } 480 | } 481 | } 482 | 483 | if newBytes := heapAllocatedBytes(); newBytes > oldBytes+overhead { 484 | t.Logf("Size=%d, Total=%d, Trie state:\n%s\n", trie.size(), trie.total(), trie.dump()) 485 | t.Errorf("Heap space leak, grew %d bytes (from %d to %d)\n", newBytes-oldBytes, oldBytes, newBytes) 486 | } 487 | } 488 | 489 | func TestParticiaTrie_DeleteNonExistent(t *testing.T) { 490 | trie := NewTrie() 491 | 492 | insertData := []testData{ 493 | {"Pepan", "Pepan Zdepan", success}, 494 | {"Honza", "Honza Novak", success}, 495 | {"Jenik", "Jenik Poustevnicek", success}, 496 | } 497 | deleteData := []testData{ 498 | {"Pepan", "Pepan Zdepan", success}, 499 | {"Honza", "Honza Novak", success}, 500 | {"Pepan", "Pepan Zdepan", failure}, 501 | {"Jenik", "Jenik Poustevnicek", success}, 502 | {"Honza", "Honza Novak", failure}, 503 | } 504 | 505 | for _, v := range insertData { 506 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 507 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 508 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 509 | } 510 | } 511 | 512 | for _, v := range deleteData { 513 | t.Logf("DELETE word=%v, success=%v", v.key, v.retVal) 514 | if ok := trie.Delete([]byte(v.key)); ok != v.retVal { 515 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 516 | } 517 | } 518 | } 519 | 520 | func TestParticiaTrie_DeleteSubtree(t *testing.T) { 521 | trie := NewTrie() 522 | 523 | insertData := []testData{ 524 | {"P", 0, success}, 525 | {"Pe", 1, success}, 526 | {"Pep", 2, success}, 527 | {"Pepa", 3, success}, 528 | {"Pepa Zdepa", 4, success}, 529 | {"Pepa Kuchar", 5, success}, 530 | {"Honza", 6, success}, 531 | {"Jenik", 7, success}, 532 | } 533 | deleteData := []testData{ 534 | {"Pe", -1, success}, 535 | {"Pe", -1, failure}, 536 | {"Honzik", -1, failure}, 537 | {"Honza", -1, success}, 538 | {"Honza", -1, failure}, 539 | {"Pep", -1, failure}, 540 | {"P", -1, success}, 541 | {"Nobody", -1, failure}, 542 | {"", -1, success}, 543 | } 544 | 545 | for _, v := range insertData { 546 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 547 | if ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 548 | t.Fatalf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 549 | } 550 | } 551 | 552 | for _, v := range deleteData { 553 | t.Logf("DELETE_SUBTREE prefix=%v, success=%v", v.key, v.retVal) 554 | if ok := trie.DeleteSubtree([]byte(v.key)); ok != v.retVal { 555 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 556 | } 557 | } 558 | } 559 | 560 | /* 561 | func TestTrie_Dump(t *testing.T) { 562 | trie := NewTrie() 563 | 564 | data := []testData{ 565 | {"Honda", nil, success}, 566 | {"Honza", nil, success}, 567 | {"Jenik", nil, success}, 568 | {"Pepan", nil, success}, 569 | {"Pepin", nil, success}, 570 | } 571 | 572 | for i, v := range data { 573 | if _, ok := trie.Insert([]byte(v.key), v.value); ok != v.retVal { 574 | t.Logf("INSERT %v %v", v.key, v.value) 575 | t.Fatalf("Unexpected return value, expected=%v, got=%v", i, ok) 576 | } 577 | } 578 | 579 | dump := ` 580 | +--+--+ Hon +--+--+ da 581 | | | 582 | | +--+ za 583 | | 584 | +--+ Jenik 585 | | 586 | +--+ Pep +--+--+ an 587 | | 588 | +--+ in 589 | ` 590 | 591 | var buf bytes.Buffer 592 | trie.Dump(buf) 593 | 594 | if !bytes.Equal(buf.Bytes(), dump) { 595 | t.Logf("DUMP") 596 | t.Fatalf("Unexpected dump generated, expected\n\n%v\ngot\n\n%v", dump, buf.String()) 597 | } 598 | } 599 | */ 600 | 601 | func TestTrie_compact(t *testing.T) { 602 | trie := NewTrie() 603 | 604 | trie.Insert(Prefix("a"), 0) 605 | trie.Insert(Prefix("ab"), 0) 606 | trie.Insert(Prefix("abc"), 0) 607 | trie.Insert(Prefix("abcd"), 0) 608 | trie.Insert(Prefix("abcde"), 0) 609 | trie.Insert(Prefix("abcdef"), 0) 610 | trie.Insert(Prefix("abcdefg"), 0) 611 | trie.Insert(Prefix("abcdefgi"), 0) 612 | trie.Insert(Prefix("abcdefgij"), 0) 613 | trie.Insert(Prefix("abcdefgijk"), 0) 614 | 615 | trie.Delete(Prefix("abcdef")) 616 | trie.Delete(Prefix("abcde")) 617 | trie.Delete(Prefix("abcdefg")) 618 | 619 | trie.Delete(Prefix("a")) 620 | trie.Delete(Prefix("abc")) 621 | trie.Delete(Prefix("ab")) 622 | 623 | trie.Visit(func(prefix Prefix, item Item) error { 624 | // 97 ~~ 'a', 625 | for ch := byte(97); ch <= 107; ch++ { 626 | if c := bytes.Count(prefix, []byte{ch}); c > 1 { 627 | t.Errorf("%q appeared in %q %v times", ch, prefix, c) 628 | } 629 | } 630 | return nil 631 | }) 632 | } 633 | 634 | func TestTrie_longestCommonPrefixLength(t *testing.T) { 635 | type args struct { 636 | prefix Prefix 637 | caseInsensitive bool 638 | } 639 | 640 | tests := []struct { 641 | prefix Prefix 642 | args args 643 | want int 644 | }{ 645 | { 646 | Prefix("1234567890"), 647 | args{ 648 | Prefix(""), 649 | false, 650 | }, 651 | 0, 652 | }, 653 | { 654 | Prefix("1234567890"), 655 | args{ 656 | Prefix("12345"), 657 | false, 658 | }, 659 | 5, 660 | }, 661 | { 662 | Prefix("1234567890"), 663 | args{ 664 | Prefix("123789"), 665 | false, 666 | }, 667 | 3, 668 | }, 669 | { 670 | Prefix("1234567890"), 671 | args{ 672 | Prefix("12345678901"), 673 | false, 674 | }, 675 | 10, 676 | }, 677 | { 678 | 679 | Prefix("aBcDeFg"), 680 | args{ 681 | Prefix("abcd"), 682 | true, 683 | }, 684 | 4, 685 | }, 686 | { 687 | 688 | Prefix("aBcDeFg"), 689 | args{ 690 | Prefix("ABCDEF"), 691 | true, 692 | }, 693 | 6, 694 | }, 695 | { 696 | 697 | Prefix("eeffgghh"), 698 | args{ 699 | Prefix("eEfFGgH"), 700 | true, 701 | }, 702 | 7, 703 | }, 704 | } 705 | 706 | for _, test := range tests { 707 | trie := NewTrie() 708 | trie.prefix = test.prefix 709 | 710 | ret := trie.longestCommonPrefixLength(test.args.prefix, 711 | test.args.caseInsensitive) 712 | 713 | if ret != test.want { 714 | t.Errorf("unexpected return value, expected %d, got %d", test.want, ret) 715 | } 716 | } 717 | } 718 | 719 | // Examples -------------------------------------------------------------------- 720 | 721 | func ExampleTrie() { 722 | // Create a new tree. 723 | trie := NewTrie() 724 | 725 | // Insert some items. 726 | trie.Insert(Prefix("Pepa Novak"), 1) 727 | trie.Insert(Prefix("Pepa Sindelar"), 2) 728 | trie.Insert(Prefix("Karel Macha"), 3) 729 | trie.Insert(Prefix("Karel Hynek Macha"), 4) 730 | 731 | // Just check if some things are present in the tree. 732 | key := Prefix("Pepa Novak") 733 | fmt.Printf("%q present? %v\n", key, trie.Match(key)) 734 | key = Prefix("Karel") 735 | fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key)) 736 | 737 | // Walk the tree. 738 | trie.Visit(printItem) 739 | // "Karel Hynek Macha": 4 740 | // "Karel Macha": 3 741 | // "Pepa Novak": 1 742 | // "Pepa Sindelar": 2 743 | 744 | // Walk a subtree. 745 | trie.VisitSubtree(Prefix("Pepa"), printItem) 746 | // "Pepa Novak": 1 747 | // "Pepa Sindelar": 2 748 | 749 | // Modify an item, then fetch it from the tree. 750 | trie.Set(Prefix("Karel Hynek Macha"), 10) 751 | key = Prefix("Karel Hynek Macha") 752 | fmt.Printf("%q: %v\n", key, trie.Get(key)) 753 | // "Karel Hynek Macha": 10 754 | 755 | // Walk prefixes. 756 | prefix := Prefix("Karel Hynek Macha je kouzelnik") 757 | trie.VisitPrefixes(prefix, false, printItem) 758 | // "Karel Hynek Macha": 10 759 | 760 | // Delete some items. 761 | trie.Delete(Prefix("Pepa Novak")) 762 | trie.Delete(Prefix("Karel Macha")) 763 | 764 | // Walk again. 765 | trie.Visit(printItem) 766 | // "Karel Hynek Macha": 10 767 | // "Pepa Sindelar": 2 768 | 769 | // Delete a subtree. 770 | trie.DeleteSubtree(Prefix("Pepa")) 771 | 772 | // Print what is left. 773 | trie.Visit(printItem) 774 | // "Karel Hynek Macha": 10 775 | 776 | // Output: 777 | // "Pepa Novak" present? true 778 | // Anybody called "Karel" here? true 779 | // "Pepa Novak": 1 780 | // "Pepa Sindelar": 2 781 | // "Karel Macha": 3 782 | // "Karel Hynek Macha": 4 783 | // "Pepa Novak": 1 784 | // "Pepa Sindelar": 2 785 | // "Karel Hynek Macha": 10 786 | // "Karel Hynek Macha": 10 787 | // "Pepa Sindelar": 2 788 | // "Karel Hynek Macha": 10 789 | // "Karel Hynek Macha": 10 790 | } 791 | 792 | // Helpers --------------------------------------------------------------------- 793 | 794 | func printItem(prefix Prefix, item Item) error { 795 | fmt.Printf("%q: %v\n", prefix, item) 796 | return nil 797 | } 798 | -------------------------------------------------------------------------------- /patricia/patricia_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The go-patricia AUTHORS 2 | // 3 | // Use of this source code is governed by The MIT License 4 | // that can be found in the LICENSE file. 5 | 6 | package patricia 7 | 8 | import ( 9 | "crypto/rand" 10 | mrand "math/rand" 11 | "reflect" 12 | "testing" 13 | ) 14 | 15 | // Tests ----------------------------------------------------------------------- 16 | 17 | func TestTrie_GetNonexistentPrefix(t *testing.T) { 18 | trie := NewTrie() 19 | 20 | data := []testData{ 21 | {"aba", 0, success}, 22 | } 23 | 24 | for _, v := range data { 25 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 26 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 27 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 28 | } 29 | } 30 | 31 | t.Logf("GET prefix=baa, expect item=nil") 32 | if item := trie.Get(Prefix("baa")); item != nil { 33 | t.Errorf("Unexpected return value, expected=, got=%v", item) 34 | } 35 | } 36 | 37 | func TestTrie_RandomKitchenSink(t *testing.T) { 38 | if testing.Short() { 39 | t.Skip() 40 | } 41 | const count, size = 750000, 16 42 | b := make([]byte, count+size+1) 43 | if _, err := rand.Read(b); err != nil { 44 | t.Fatal("error generating random bytes", err) 45 | } 46 | m := make(map[string]string) 47 | for i := 0; i < count; i++ { 48 | m[string(b[i:i+size])] = string(b[i+1 : i+size+1]) 49 | } 50 | trie := NewTrie() 51 | getAndDelete := func(k, v string) { 52 | i := trie.Get(Prefix(k)) 53 | if i == nil { 54 | t.Fatalf("item not found, prefix=%v", []byte(k)) 55 | } else if s, ok := i.(string); !ok { 56 | t.Fatalf("unexpected item type, expecting=%v, got=%v", reflect.TypeOf(k), reflect.TypeOf(i)) 57 | } else if s != v { 58 | t.Fatalf("unexpected item, expecting=%v, got=%v", []byte(k), []byte(s)) 59 | } else if !trie.Delete(Prefix(k)) { 60 | t.Fatalf("delete failed, prefix=%v", []byte(k)) 61 | } else if i = trie.Get(Prefix(k)); i != nil { 62 | t.Fatalf("unexpected item, expecting=, got=%v", i) 63 | } else if trie.Delete(Prefix(k)) { 64 | t.Fatalf("extra delete succeeded, prefix=%v", []byte(k)) 65 | } 66 | } 67 | for k, v := range m { 68 | if !trie.Insert(Prefix(k), v) { 69 | t.Fatalf("insert failed, prefix=%v", []byte(k)) 70 | } 71 | if byte(k[size/2]) < 128 { 72 | getAndDelete(k, v) 73 | delete(m, k) 74 | } 75 | } 76 | for k, v := range m { 77 | getAndDelete(k, v) 78 | } 79 | } 80 | 81 | // Make sure Delete that affects the root node works. 82 | // This was panicking when Delete was broken. 83 | func TestTrie_DeleteRoot(t *testing.T) { 84 | trie := NewTrie() 85 | 86 | v := testData{"aba", 0, success} 87 | 88 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 89 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 90 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 91 | } 92 | 93 | t.Logf("DELETE prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 94 | if ok := trie.Delete(Prefix(v.key)); ok != v.retVal { 95 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 96 | } 97 | } 98 | 99 | func TestTrie_DeleteAbsentPrefix(t *testing.T) { 100 | trie := NewTrie() 101 | 102 | v := testData{"a", 0, success} 103 | 104 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 105 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 106 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 107 | } 108 | 109 | d := "ab" 110 | t.Logf("DELETE prefix=%v, success=%v", d, failure) 111 | if ok := trie.Delete(Prefix(d)); ok != failure { 112 | t.Errorf("Unexpected return value, expected=%v, got=%v", failure, ok) 113 | } 114 | t.Logf("GET prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 115 | if i := trie.Get(Prefix(v.key)); i != v.value { 116 | t.Errorf("Unexpected item, expected=%v, got=%v", v.value, i) 117 | } 118 | } 119 | 120 | func reverse(s string) string { 121 | runes := []rune(s) 122 | for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { 123 | runes[i], runes[j] = runes[j], runes[i] 124 | } 125 | return string(runes) 126 | } 127 | 128 | func checkMasksRecursive(t *testing.T, root *Trie) { 129 | for _, child := range root.children.getChildren() { 130 | if child.mask & ^root.mask != 0 { 131 | t.Errorf("\ninvalid mask at prefix %s\nchild prefix: %s\ncharmap: \t%s\nmask: \t%064b\n"+ 132 | "child mask: \t%064b\ndiff:\t%064b\n", 133 | root.prefix, 134 | child.prefix, 135 | reverse(charmap), 136 | root.mask, 137 | child.mask, 138 | child.mask & ^root.mask, 139 | ) 140 | } 141 | checkMasksRecursive(t, child) 142 | } 143 | } 144 | 145 | func TestTrie_AddCorrectMasks(t *testing.T) { 146 | trie := NewTrie() 147 | data := []testData{ 148 | {"Pepan", "Pepan Zdepan", success}, 149 | {"Pepin", "Pepin Omacka", success}, 150 | {"Honza", "Honza Novak", success}, 151 | {"Jenik", "Jenik Poustevnicek", success}, 152 | {"Pepan", "Pepan Dupan", failure}, 153 | {"Karel", "Karel Pekar", success}, 154 | {"Jenak", "Jenak Poustevnicek", success}, 155 | {"Pepanek", "Pepanek Zemlicka", success}, 156 | } 157 | 158 | for _, v := range data { 159 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 160 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 161 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 162 | } 163 | checkMasksRecursive(t, trie) 164 | } 165 | } 166 | 167 | func TestTrie_DeleteCorrectMasks(t *testing.T) { 168 | data := []testData{ 169 | {"Pepan", "Pepan Zdepan", success}, 170 | {"Pepin", "Pepin Omacka", success}, 171 | {"Honza", "Honza Novak", success}, 172 | {"Jenik", "Jenik Poustevnicek", success}, 173 | {"Karel", "Karel Pekar", success}, 174 | {"Jenak", "Jenak Poustevnicek", success}, 175 | {"Pepanek", "Pepanek Zemlicka", success}, 176 | } 177 | 178 | deleteData := [][]testData{ 179 | { 180 | {"Honza", "Honza Novak", success}, 181 | {"Jenik", "Jenik Poustevnicek", success}, 182 | {"Pepan", "Pepan Dupan", success}, 183 | }, 184 | { 185 | {"Pepan", "Pepan Dupan", success}, 186 | }, 187 | { 188 | {"Jenak", "Jenak Poustevnicek", success}, 189 | {"Pepanek", "Pepanek Zemlicka", success}, 190 | {"Pepin", "Pepin Omacka", success}, 191 | {"Honza", "Honza Novak", success}, 192 | {"Jenik", "Jenik Poustevnicek", success}, 193 | }, 194 | } 195 | 196 | for _, d := range deleteData { 197 | trie := NewTrie() 198 | for _, v := range data { 199 | t.Logf("INSERT prefix=%v, item=%v, success=%v", v.key, v.value, v.retVal) 200 | if ok := trie.Insert(Prefix(v.key), v.value); ok != v.retVal { 201 | t.Errorf("Unexpected return value, expected=%v, got=%v", v.retVal, ok) 202 | } 203 | } 204 | 205 | for _, record := range d { 206 | trie.Delete(Prefix(record.key)) 207 | } 208 | 209 | checkMasksRecursive(t, trie) 210 | } 211 | 212 | } 213 | 214 | func populateTrie(t *testing.T) *Trie { 215 | data := []string{ 216 | "Pepan", 217 | "Pepin", 218 | "Honza", 219 | "Jenik", 220 | "Karel", 221 | "Jenak", 222 | "Pepanek", 223 | } 224 | 225 | trie := NewTrie() 226 | for _, v := range data { 227 | if ok := trie.Insert(Prefix(v), struct{}{}); !ok { 228 | t.Errorf("Couldn't insert item %s", v) 229 | } 230 | } 231 | 232 | return trie 233 | } 234 | 235 | func TestTrie_FuzzyCollect(t *testing.T) { 236 | trie := populateTrie(t) 237 | 238 | type testResult struct { 239 | wantKey string 240 | wantSkipped int 241 | } 242 | 243 | type testData struct { 244 | query string 245 | caseInsensitive bool 246 | wantResults []testResult 247 | } 248 | 249 | testQueries := []testData{ 250 | { 251 | "Ppn", 252 | false, 253 | []testResult{ 254 | {"Pepan", 2}, 255 | {"Pepin", 2}, 256 | {"Pepanek", 2}, 257 | }, 258 | }, 259 | { 260 | "Ha", 261 | false, 262 | []testResult{ 263 | {"Honza", 3}, 264 | }, 265 | }, 266 | { 267 | "nza", 268 | false, 269 | []testResult{ 270 | {"Honza", 0}, 271 | }, 272 | }, 273 | { 274 | "eni", 275 | false, 276 | []testResult{ 277 | {"Jenik", 0}, 278 | }, 279 | }, 280 | { 281 | "jk", 282 | true, 283 | []testResult{ 284 | {"Jenik", 3}, 285 | {"Jenak", 3}, 286 | }, 287 | }, 288 | { 289 | "ppn", 290 | true, 291 | []testResult{ 292 | {"Pepan", 2}, 293 | {"Pepin", 2}, 294 | {"Pepanek", 2}, 295 | }, 296 | }, 297 | } 298 | 299 | for _, data := range testQueries { 300 | resultMap := make(map[string]int) 301 | t.Logf("QUERY %s", data.query) 302 | trie.VisitFuzzy(Prefix(data.query), data.caseInsensitive, func(prefix Prefix, item Item, skipped int) error { 303 | // result := testResult{string(prefix), skipped} 304 | resultMap[string(prefix)] = skipped 305 | return nil 306 | }) 307 | t.Logf("got result set %v\n", resultMap) 308 | 309 | for _, want := range data.wantResults { 310 | got, ok := resultMap[want.wantKey] 311 | if !ok { 312 | t.Errorf("item %s not found in result set\n", want.wantKey) 313 | continue 314 | } 315 | 316 | if got != want.wantSkipped { 317 | t.Errorf("got wrong skipped value, wanted %d, got %d\n", 318 | want.wantSkipped, got) 319 | } 320 | } 321 | } 322 | } 323 | 324 | func TestTrie_SubstringCollect(t *testing.T) { 325 | trie := populateTrie(t) 326 | 327 | type testData struct { 328 | query string 329 | caseInsensitive bool 330 | wantResults []string 331 | } 332 | 333 | testQueries := []testData{ 334 | { 335 | "epa", 336 | false, 337 | []string{ 338 | "Pepan", 339 | "Pepanek", 340 | }, 341 | }, 342 | { 343 | "onza", 344 | false, 345 | []string{ 346 | "Honza", 347 | }, 348 | }, 349 | { 350 | "nza", 351 | false, 352 | []string{ 353 | "Honza", 354 | }, 355 | }, 356 | { 357 | "l", 358 | false, 359 | []string{ 360 | "Karel", 361 | }, 362 | }, 363 | { 364 | "a", 365 | false, 366 | []string{ 367 | "Pepan", 368 | "Honza", 369 | "Pepan", 370 | "Karel", 371 | "Jenak", 372 | "Pepanek", 373 | }, 374 | }, 375 | { 376 | "pep", 377 | true, 378 | []string{ 379 | "Pepin", 380 | "Pepan", 381 | }, 382 | }, 383 | { 384 | "kar", 385 | true, 386 | []string{ 387 | "Karel", 388 | }, 389 | }, 390 | { 391 | "", 392 | false, 393 | []string{ 394 | "Pepan", 395 | "Pepin", 396 | "Honza", 397 | "Jenik", 398 | "Karel", 399 | "Jenak", 400 | "Pepanek", 401 | }, 402 | }, 403 | } 404 | 405 | for _, data := range testQueries { 406 | resultMap := make(map[string]bool) 407 | t.Logf("QUERY %s", data.query) 408 | trie.VisitSubstring(Prefix(data.query), true, func(prefix Prefix, item Item) error { 409 | // result := testResult{string(prefix), skipped} 410 | resultMap[string(prefix)] = true 411 | return nil 412 | }) 413 | t.Logf("got result set %v\n", resultMap) 414 | 415 | for _, want := range data.wantResults { 416 | if _, ok := resultMap[want]; !ok { 417 | t.Errorf("item %s not found in result set\n", want) 418 | continue 419 | } 420 | } 421 | } 422 | } 423 | 424 | func Test_makePrefixMask(t *testing.T) { 425 | type testData struct { 426 | key Prefix 427 | wanted uint64 428 | } 429 | 430 | data := []testData{ 431 | { 432 | Prefix("0123456789"), 433 | 0x3FF, 434 | }, 435 | { 436 | Prefix("AAAA"), 437 | 0x400, 438 | }, 439 | { 440 | Prefix(""), 441 | 0, 442 | }, 443 | { 444 | Prefix("abc"), 445 | 0x7000000000, 446 | }, 447 | { 448 | Prefix(".-"), 449 | 0xc000000000000000, 450 | }, 451 | } 452 | 453 | for _, d := range data { 454 | got := makePrefixMask(d.key) 455 | if got != d.wanted { 456 | t.Errorf("Unexpected bitmask, wanted: %b, got %b\n", d.wanted, got) 457 | } 458 | } 459 | } 460 | 461 | const ( 462 | amountWords = 100000 463 | wordLength = 10 464 | queryLength = 10 465 | ) 466 | 467 | var benchmarkTrie *Trie 468 | 469 | func populateBenchmarkTrie(superDenseChildList bool) { 470 | benchmarkTrie = NewTrie() 471 | 472 | for i := 0; i < amountWords; i++ { 473 | benchmarkTrie.Insert(Prefix(mrandBytes(wordLength)), struct{}{}) 474 | } 475 | } 476 | 477 | type visitFunc func(prefix Prefix, caseInsensitive bool, visitor VisitorFunc) error 478 | 479 | func benchmarkVisit(caseInsensitive bool, visitor visitFunc, b *testing.B) { 480 | b.ResetTimer() 481 | 482 | for i := 0; i < b.N; i++ { 483 | visitor(Prefix(mrandBytes(queryLength)), caseInsensitive, func(prefix Prefix, item Item) error { 484 | return nil 485 | }) 486 | } 487 | } 488 | 489 | func benchmarkVisitFuzzy(caseInsensitive bool, b *testing.B) { 490 | b.ResetTimer() 491 | 492 | for i := 0; i < b.N; i++ { 493 | benchmarkTrie.VisitFuzzy(Prefix(mrandBytes(queryLength)), caseInsensitive, func(prefix Prefix, item Item, skipped int) error { 494 | return nil 495 | }) 496 | } 497 | } 498 | 499 | func BenchmarkPrefix(b *testing.B) { 500 | populateBenchmarkTrie(false) 501 | benchmarkVisit(false, benchmarkTrie.VisitPrefixes, b) 502 | } 503 | func BenchmarkPrefixCaseInsensitive(b *testing.B) { 504 | populateBenchmarkTrie(false) 505 | benchmarkVisit(true, benchmarkTrie.VisitPrefixes, b) 506 | } 507 | func BenchmarkPrefixSuperDense(b *testing.B) { 508 | populateBenchmarkTrie(true) 509 | benchmarkVisit(false, benchmarkTrie.VisitPrefixes, b) 510 | } 511 | func BenchmarkPrefixCaseInsensitiveSuperDense(b *testing.B) { 512 | populateBenchmarkTrie(true) 513 | benchmarkVisit(true, benchmarkTrie.VisitPrefixes, b) 514 | } 515 | func BenchmarkSubstring(b *testing.B) { 516 | populateBenchmarkTrie(false) 517 | benchmarkVisit(false, benchmarkTrie.VisitSubstring, b) 518 | } 519 | func BenchmarkSubstringCaseInsensitive(b *testing.B) { 520 | populateBenchmarkTrie(false) 521 | benchmarkVisit(true, benchmarkTrie.VisitSubstring, b) 522 | } 523 | func BenchmarkSubstringSuperDense(b *testing.B) { 524 | populateBenchmarkTrie(true) 525 | benchmarkVisit(false, benchmarkTrie.VisitSubstring, b) 526 | } 527 | func BenchmarkSubstringCaseInsensitiveSuperDense(b *testing.B) { 528 | populateBenchmarkTrie(true) 529 | benchmarkVisit(true, benchmarkTrie.VisitSubstring, b) 530 | } 531 | 532 | func BenchmarkFuzzy(b *testing.B) { 533 | populateBenchmarkTrie(false) 534 | benchmarkVisitFuzzy(false, b) 535 | } 536 | func BenchmarkFuzzyCaseInsensitive(b *testing.B) { 537 | populateBenchmarkTrie(false) 538 | benchmarkVisitFuzzy(true, b) 539 | } 540 | func BenchmarkFuzzySuperDense(b *testing.B) { 541 | populateBenchmarkTrie(true) 542 | benchmarkVisitFuzzy(false, b) 543 | } 544 | func BenchmarkFuzzyCaseInsensitiveSuperDense(b *testing.B) { 545 | populateBenchmarkTrie(true) 546 | benchmarkVisitFuzzy(true, b) 547 | } 548 | 549 | func mrandBytes(length int) []byte { 550 | bytes := make([]byte, length) 551 | for i := 0; i < length; i++ { 552 | bytes = append(bytes, byte(mrand.Intn(75)+'0')) 553 | } 554 | 555 | return bytes 556 | } 557 | --------------------------------------------------------------------------------