├── go.mod ├── .travis.yml ├── .gitignore ├── LICENSE ├── README.md ├── radix_test.go └── radix.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/armon/go-radix 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | arch: 3 | - amd64 4 | - ppc64le 5 | go: 6 | - tip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Armon Dadgar 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 | go-radix [![Build Status](https://travis-ci.org/armon/go-radix.png)](https://travis-ci.org/armon/go-radix) 2 | ========= 3 | 4 | Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree). 5 | The package only provides a single `Tree` implementation, optimized for sparse nodes. 6 | 7 | As a radix tree, it provides the following: 8 | * O(k) operations. In many cases, this can be faster than a hash table since 9 | the hash function is an O(k) operation, and hash tables have very poor cache locality. 10 | * Minimum / Maximum value lookups 11 | * Ordered iteration 12 | 13 | For an immutable variant, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix). 14 | 15 | Documentation 16 | ============= 17 | 18 | The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix). 19 | 20 | Example 21 | ======= 22 | 23 | Below is a simple example of usage 24 | 25 | ```go 26 | // Create a tree 27 | r := radix.New() 28 | r.Insert("foo", 1) 29 | r.Insert("bar", 2) 30 | r.Insert("foobar", 2) 31 | 32 | // Find the longest prefix match 33 | m, _, _ := r.LongestPrefix("foozip") 34 | if m != "foo" { 35 | panic("should be foo") 36 | } 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /radix_test.go: -------------------------------------------------------------------------------- 1 | package radix 2 | 3 | import ( 4 | crand "crypto/rand" 5 | "fmt" 6 | "reflect" 7 | "sort" 8 | "strconv" 9 | "testing" 10 | ) 11 | 12 | func TestRadix(t *testing.T) { 13 | var min, max string 14 | inp := make(map[string]interface{}) 15 | for i := 0; i < 1000; i++ { 16 | gen := generateUUID() 17 | inp[gen] = i 18 | if gen < min || i == 0 { 19 | min = gen 20 | } 21 | if gen > max || i == 0 { 22 | max = gen 23 | } 24 | } 25 | 26 | r := NewFromMap(inp) 27 | if r.Len() != len(inp) { 28 | t.Fatalf("bad length: %v %v", r.Len(), len(inp)) 29 | } 30 | 31 | r.Walk(func(k string, v interface{}) bool { 32 | println(k) 33 | return false 34 | }) 35 | 36 | for k, v := range inp { 37 | out, ok := r.Get(k) 38 | if !ok { 39 | t.Fatalf("missing key: %v", k) 40 | } 41 | if out != v { 42 | t.Fatalf("value mis-match: %v %v", out, v) 43 | } 44 | } 45 | 46 | // Check min and max 47 | outMin, _, _ := r.Minimum() 48 | if outMin != min { 49 | t.Fatalf("bad minimum: %v %v", outMin, min) 50 | } 51 | outMax, _, _ := r.Maximum() 52 | if outMax != max { 53 | t.Fatalf("bad maximum: %v %v", outMax, max) 54 | } 55 | 56 | for k, v := range inp { 57 | out, ok := r.Delete(k) 58 | if !ok { 59 | t.Fatalf("missing key: %v", k) 60 | } 61 | if out != v { 62 | t.Fatalf("value mis-match: %v %v", out, v) 63 | } 64 | } 65 | if r.Len() != 0 { 66 | t.Fatalf("bad length: %v", r.Len()) 67 | } 68 | } 69 | 70 | func TestRoot(t *testing.T) { 71 | r := New() 72 | _, ok := r.Delete("") 73 | if ok { 74 | t.Fatalf("bad") 75 | } 76 | _, ok = r.Insert("", true) 77 | if ok { 78 | t.Fatalf("bad") 79 | } 80 | val, ok := r.Get("") 81 | if !ok || val != true { 82 | t.Fatalf("bad: %v", val) 83 | } 84 | val, ok = r.Delete("") 85 | if !ok || val != true { 86 | t.Fatalf("bad: %v", val) 87 | } 88 | } 89 | 90 | func TestDelete(t *testing.T) { 91 | 92 | r := New() 93 | 94 | s := []string{"", "A", "AB"} 95 | 96 | for _, ss := range s { 97 | r.Insert(ss, true) 98 | } 99 | 100 | for _, ss := range s { 101 | _, ok := r.Delete(ss) 102 | if !ok { 103 | t.Fatalf("bad %q", ss) 104 | } 105 | } 106 | } 107 | 108 | func TestDeletePrefix(t *testing.T) { 109 | type exp struct { 110 | inp []string 111 | prefix string 112 | out []string 113 | numDeleted int 114 | } 115 | 116 | cases := []exp{ 117 | {[]string{"", "A", "AB", "ABC", "R", "S"}, "A", []string{"", "R", "S"}, 3}, 118 | {[]string{"", "A", "AB", "ABC", "R", "S"}, "ABC", []string{"", "A", "AB", "R", "S"}, 1}, 119 | {[]string{"", "A", "AB", "ABC", "R", "S"}, "", []string{}, 6}, 120 | {[]string{"", "A", "AB", "ABC", "R", "S"}, "S", []string{"", "A", "AB", "ABC", "R"}, 1}, 121 | {[]string{"", "A", "AB", "ABC", "R", "S"}, "SS", []string{"", "A", "AB", "ABC", "R", "S"}, 0}, 122 | } 123 | 124 | for _, test := range cases { 125 | r := New() 126 | for _, ss := range test.inp { 127 | r.Insert(ss, true) 128 | } 129 | 130 | deleted := r.DeletePrefix(test.prefix) 131 | if deleted != test.numDeleted { 132 | t.Fatalf("Bad delete, expected %v to be deleted but got %v", test.numDeleted, deleted) 133 | } 134 | 135 | out := []string{} 136 | fn := func(s string, v interface{}) bool { 137 | out = append(out, s) 138 | return false 139 | } 140 | r.Walk(fn) 141 | 142 | if !reflect.DeepEqual(out, test.out) { 143 | t.Fatalf("mis-match: %v %v", out, test.out) 144 | } 145 | } 146 | } 147 | 148 | func TestLongestPrefix(t *testing.T) { 149 | r := New() 150 | 151 | keys := []string{ 152 | "", 153 | "foo", 154 | "foobar", 155 | "foobarbaz", 156 | "foobarbazzip", 157 | "foozip", 158 | } 159 | for _, k := range keys { 160 | r.Insert(k, nil) 161 | } 162 | if r.Len() != len(keys) { 163 | t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 164 | } 165 | 166 | type exp struct { 167 | inp string 168 | out string 169 | } 170 | cases := []exp{ 171 | {"a", ""}, 172 | {"abc", ""}, 173 | {"fo", ""}, 174 | {"foo", "foo"}, 175 | {"foob", "foo"}, 176 | {"foobar", "foobar"}, 177 | {"foobarba", "foobar"}, 178 | {"foobarbaz", "foobarbaz"}, 179 | {"foobarbazzi", "foobarbaz"}, 180 | {"foobarbazzip", "foobarbazzip"}, 181 | {"foozi", "foo"}, 182 | {"foozip", "foozip"}, 183 | {"foozipzap", "foozip"}, 184 | } 185 | for _, test := range cases { 186 | m, _, ok := r.LongestPrefix(test.inp) 187 | if !ok { 188 | t.Fatalf("no match: %v", test) 189 | } 190 | if m != test.out { 191 | t.Fatalf("mis-match: %v %v", m, test) 192 | } 193 | } 194 | } 195 | 196 | func TestWalkPrefix(t *testing.T) { 197 | r := New() 198 | 199 | keys := []string{ 200 | "foobar", 201 | "foo/bar/baz", 202 | "foo/baz/bar", 203 | "foo/zip/zap", 204 | "zipzap", 205 | } 206 | for _, k := range keys { 207 | r.Insert(k, nil) 208 | } 209 | if r.Len() != len(keys) { 210 | t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 211 | } 212 | 213 | type exp struct { 214 | inp string 215 | out []string 216 | } 217 | cases := []exp{ 218 | { 219 | "f", 220 | []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 221 | }, 222 | { 223 | "foo", 224 | []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 225 | }, 226 | { 227 | "foob", 228 | []string{"foobar"}, 229 | }, 230 | { 231 | "foo/", 232 | []string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, 233 | }, 234 | { 235 | "foo/b", 236 | []string{"foo/bar/baz", "foo/baz/bar"}, 237 | }, 238 | { 239 | "foo/ba", 240 | []string{"foo/bar/baz", "foo/baz/bar"}, 241 | }, 242 | { 243 | "foo/bar", 244 | []string{"foo/bar/baz"}, 245 | }, 246 | { 247 | "foo/bar/baz", 248 | []string{"foo/bar/baz"}, 249 | }, 250 | { 251 | "foo/bar/bazoo", 252 | []string{}, 253 | }, 254 | { 255 | "z", 256 | []string{"zipzap"}, 257 | }, 258 | } 259 | 260 | for _, test := range cases { 261 | out := []string{} 262 | fn := func(s string, v interface{}) bool { 263 | out = append(out, s) 264 | return false 265 | } 266 | r.WalkPrefix(test.inp, fn) 267 | sort.Strings(out) 268 | sort.Strings(test.out) 269 | if !reflect.DeepEqual(out, test.out) { 270 | t.Fatalf("mis-match: %v %v", out, test.out) 271 | } 272 | } 273 | } 274 | 275 | func TestWalkPath(t *testing.T) { 276 | r := New() 277 | 278 | keys := []string{ 279 | "foo", 280 | "foo/bar", 281 | "foo/bar/baz", 282 | "foo/baz/bar", 283 | "foo/zip/zap", 284 | "zipzap", 285 | } 286 | for _, k := range keys { 287 | r.Insert(k, nil) 288 | } 289 | if r.Len() != len(keys) { 290 | t.Fatalf("bad len: %v %v", r.Len(), len(keys)) 291 | } 292 | 293 | type exp struct { 294 | inp string 295 | out []string 296 | } 297 | cases := []exp{ 298 | { 299 | "f", 300 | []string{}, 301 | }, 302 | { 303 | "foo", 304 | []string{"foo"}, 305 | }, 306 | { 307 | "foo/", 308 | []string{"foo"}, 309 | }, 310 | { 311 | "foo/ba", 312 | []string{"foo"}, 313 | }, 314 | { 315 | "foo/bar", 316 | []string{"foo", "foo/bar"}, 317 | }, 318 | { 319 | "foo/bar/baz", 320 | []string{"foo", "foo/bar", "foo/bar/baz"}, 321 | }, 322 | { 323 | "foo/bar/bazoo", 324 | []string{"foo", "foo/bar", "foo/bar/baz"}, 325 | }, 326 | { 327 | "z", 328 | []string{}, 329 | }, 330 | } 331 | 332 | for _, test := range cases { 333 | out := []string{} 334 | fn := func(s string, v interface{}) bool { 335 | out = append(out, s) 336 | return false 337 | } 338 | r.WalkPath(test.inp, fn) 339 | sort.Strings(out) 340 | sort.Strings(test.out) 341 | if !reflect.DeepEqual(out, test.out) { 342 | t.Fatalf("mis-match: %v %v", out, test.out) 343 | } 344 | } 345 | } 346 | 347 | func TestWalkDelete(t *testing.T) { 348 | r := New() 349 | r.Insert("init0/0", nil) 350 | r.Insert("init0/1", nil) 351 | r.Insert("init0/2", nil) 352 | r.Insert("init0/3", nil) 353 | r.Insert("init1/0", nil) 354 | r.Insert("init1/1", nil) 355 | r.Insert("init1/2", nil) 356 | r.Insert("init1/3", nil) 357 | r.Insert("init2", nil) 358 | 359 | deleteFn := func(s string, v interface{}) bool { 360 | r.Delete(s) 361 | return false 362 | } 363 | 364 | r.WalkPrefix("init1", deleteFn) 365 | 366 | for _, s := range []string{"init0/0", "init0/1", "init0/2", "init0/3", "init2"} { 367 | if _, ok := r.Get(s); !ok { 368 | t.Fatalf("expecting to still find %q", s) 369 | } 370 | } 371 | if n := r.Len(); n != 5 { 372 | t.Fatalf("expected to find exactly 5 nodes, instead found %d: %v", n, r.ToMap()) 373 | } 374 | 375 | r.Walk(deleteFn) 376 | if n := r.Len(); n != 0 { 377 | t.Fatalf("expected to find exactly 0 nodes, instead found %d: %v", n, r.ToMap()) 378 | } 379 | } 380 | 381 | // generateUUID is used to generate a random UUID 382 | func generateUUID() string { 383 | buf := make([]byte, 16) 384 | if _, err := crand.Read(buf); err != nil { 385 | panic(fmt.Errorf("failed to read random bytes: %v", err)) 386 | } 387 | 388 | return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", 389 | buf[0:4], 390 | buf[4:6], 391 | buf[6:8], 392 | buf[8:10], 393 | buf[10:16]) 394 | } 395 | 396 | func BenchmarkInsert(b *testing.B) { 397 | r := New() 398 | for i := 0; i < 10000; i++ { 399 | r.Insert(fmt.Sprintf("init%d", i), true) 400 | } 401 | b.ResetTimer() 402 | for n := 0; n < b.N; n++ { 403 | _, updated := r.Insert(strconv.Itoa(n), true) 404 | if updated { 405 | b.Fatal("bad") 406 | } 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /radix.go: -------------------------------------------------------------------------------- 1 | package radix 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | ) 7 | 8 | // WalkFn is used when walking the tree. Takes a 9 | // key and value, returning if iteration should 10 | // be terminated. 11 | type WalkFn func(s string, v interface{}) bool 12 | 13 | // leafNode is used to represent a value 14 | type leafNode struct { 15 | key string 16 | val interface{} 17 | } 18 | 19 | // edge is used to represent an edge node 20 | type edge struct { 21 | label byte 22 | node *node 23 | } 24 | 25 | type node struct { 26 | // leaf is used to store possible leaf 27 | leaf *leafNode 28 | 29 | // prefix is the common prefix we ignore 30 | prefix string 31 | 32 | // Edges should be stored in-order for iteration. 33 | // We avoid a fully materialized slice to save memory, 34 | // since in most cases we expect to be sparse 35 | edges edges 36 | } 37 | 38 | func (n *node) isLeaf() bool { 39 | return n.leaf != nil 40 | } 41 | 42 | func (n *node) addEdge(e edge) { 43 | num := len(n.edges) 44 | idx := sort.Search(num, func(i int) bool { 45 | return n.edges[i].label >= e.label 46 | }) 47 | 48 | n.edges = append(n.edges, edge{}) 49 | copy(n.edges[idx+1:], n.edges[idx:]) 50 | n.edges[idx] = e 51 | } 52 | 53 | func (n *node) updateEdge(label byte, node *node) { 54 | num := len(n.edges) 55 | idx := sort.Search(num, func(i int) bool { 56 | return n.edges[i].label >= label 57 | }) 58 | if idx < num && n.edges[idx].label == label { 59 | n.edges[idx].node = node 60 | return 61 | } 62 | panic("replacing missing edge") 63 | } 64 | 65 | func (n *node) getEdge(label byte) *node { 66 | num := len(n.edges) 67 | idx := sort.Search(num, func(i int) bool { 68 | return n.edges[i].label >= label 69 | }) 70 | if idx < num && n.edges[idx].label == label { 71 | return n.edges[idx].node 72 | } 73 | return nil 74 | } 75 | 76 | func (n *node) delEdge(label byte) { 77 | num := len(n.edges) 78 | idx := sort.Search(num, func(i int) bool { 79 | return n.edges[i].label >= label 80 | }) 81 | if idx < num && n.edges[idx].label == label { 82 | copy(n.edges[idx:], n.edges[idx+1:]) 83 | n.edges[len(n.edges)-1] = edge{} 84 | n.edges = n.edges[:len(n.edges)-1] 85 | } 86 | } 87 | 88 | type edges []edge 89 | 90 | func (e edges) Len() int { 91 | return len(e) 92 | } 93 | 94 | func (e edges) Less(i, j int) bool { 95 | return e[i].label < e[j].label 96 | } 97 | 98 | func (e edges) Swap(i, j int) { 99 | e[i], e[j] = e[j], e[i] 100 | } 101 | 102 | func (e edges) Sort() { 103 | sort.Sort(e) 104 | } 105 | 106 | // Tree implements a radix tree. This can be treated as a 107 | // Dictionary abstract data type. The main advantage over 108 | // a standard hash map is prefix-based lookups and 109 | // ordered iteration, 110 | type Tree struct { 111 | root *node 112 | size int 113 | } 114 | 115 | // New returns an empty Tree 116 | func New() *Tree { 117 | return NewFromMap(nil) 118 | } 119 | 120 | // NewFromMap returns a new tree containing the keys 121 | // from an existing map 122 | func NewFromMap(m map[string]interface{}) *Tree { 123 | t := &Tree{root: &node{}} 124 | for k, v := range m { 125 | t.Insert(k, v) 126 | } 127 | return t 128 | } 129 | 130 | // Len is used to return the number of elements in the tree 131 | func (t *Tree) Len() int { 132 | return t.size 133 | } 134 | 135 | // longestPrefix finds the length of the shared prefix 136 | // of two strings 137 | func longestPrefix(k1, k2 string) int { 138 | max := len(k1) 139 | if l := len(k2); l < max { 140 | max = l 141 | } 142 | var i int 143 | for i = 0; i < max; i++ { 144 | if k1[i] != k2[i] { 145 | break 146 | } 147 | } 148 | return i 149 | } 150 | 151 | // Insert is used to add a newentry or update 152 | // an existing entry. Returns true if an existing record is updated. 153 | func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { 154 | var parent *node 155 | n := t.root 156 | search := s 157 | for { 158 | // Handle key exhaution 159 | if len(search) == 0 { 160 | if n.isLeaf() { 161 | old := n.leaf.val 162 | n.leaf.val = v 163 | return old, true 164 | } 165 | 166 | n.leaf = &leafNode{ 167 | key: s, 168 | val: v, 169 | } 170 | t.size++ 171 | return nil, false 172 | } 173 | 174 | // Look for the edge 175 | parent = n 176 | n = n.getEdge(search[0]) 177 | 178 | // No edge, create one 179 | if n == nil { 180 | e := edge{ 181 | label: search[0], 182 | node: &node{ 183 | leaf: &leafNode{ 184 | key: s, 185 | val: v, 186 | }, 187 | prefix: search, 188 | }, 189 | } 190 | parent.addEdge(e) 191 | t.size++ 192 | return nil, false 193 | } 194 | 195 | // Determine longest prefix of the search key on match 196 | commonPrefix := longestPrefix(search, n.prefix) 197 | if commonPrefix == len(n.prefix) { 198 | search = search[commonPrefix:] 199 | continue 200 | } 201 | 202 | // Split the node 203 | t.size++ 204 | child := &node{ 205 | prefix: search[:commonPrefix], 206 | } 207 | parent.updateEdge(search[0], child) 208 | 209 | // Restore the existing node 210 | child.addEdge(edge{ 211 | label: n.prefix[commonPrefix], 212 | node: n, 213 | }) 214 | n.prefix = n.prefix[commonPrefix:] 215 | 216 | // Create a new leaf node 217 | leaf := &leafNode{ 218 | key: s, 219 | val: v, 220 | } 221 | 222 | // If the new key is a subset, add to this node 223 | search = search[commonPrefix:] 224 | if len(search) == 0 { 225 | child.leaf = leaf 226 | return nil, false 227 | } 228 | 229 | // Create a new edge for the node 230 | child.addEdge(edge{ 231 | label: search[0], 232 | node: &node{ 233 | leaf: leaf, 234 | prefix: search, 235 | }, 236 | }) 237 | return nil, false 238 | } 239 | } 240 | 241 | // Delete is used to delete a key, returning the previous 242 | // value and if it was deleted 243 | func (t *Tree) Delete(s string) (interface{}, bool) { 244 | var parent *node 245 | var label byte 246 | n := t.root 247 | search := s 248 | for { 249 | // Check for key exhaution 250 | if len(search) == 0 { 251 | if !n.isLeaf() { 252 | break 253 | } 254 | goto DELETE 255 | } 256 | 257 | // Look for an edge 258 | parent = n 259 | label = search[0] 260 | n = n.getEdge(label) 261 | if n == nil { 262 | break 263 | } 264 | 265 | // Consume the search prefix 266 | if strings.HasPrefix(search, n.prefix) { 267 | search = search[len(n.prefix):] 268 | } else { 269 | break 270 | } 271 | } 272 | return nil, false 273 | 274 | DELETE: 275 | // Delete the leaf 276 | leaf := n.leaf 277 | n.leaf = nil 278 | t.size-- 279 | 280 | // Check if we should delete this node from the parent 281 | if parent != nil && len(n.edges) == 0 { 282 | parent.delEdge(label) 283 | } 284 | 285 | // Check if we should merge this node 286 | if n != t.root && len(n.edges) == 1 { 287 | n.mergeChild() 288 | } 289 | 290 | // Check if we should merge the parent's other child 291 | if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { 292 | parent.mergeChild() 293 | } 294 | 295 | return leaf.val, true 296 | } 297 | 298 | // DeletePrefix is used to delete the subtree under a prefix 299 | // Returns how many nodes were deleted 300 | // Use this to delete large subtrees efficiently 301 | func (t *Tree) DeletePrefix(s string) int { 302 | return t.deletePrefix(nil, t.root, s) 303 | } 304 | 305 | // delete does a recursive deletion 306 | func (t *Tree) deletePrefix(parent, n *node, prefix string) int { 307 | // Check for key exhaustion 308 | if len(prefix) == 0 { 309 | // Remove the leaf node 310 | subTreeSize := 0 311 | //recursively walk from all edges of the node to be deleted 312 | recursiveWalk(n, func(s string, v interface{}) bool { 313 | subTreeSize++ 314 | return false 315 | }) 316 | if n.isLeaf() { 317 | n.leaf = nil 318 | } 319 | n.edges = nil // deletes the entire subtree 320 | 321 | // Check if we should merge the parent's other child 322 | if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { 323 | parent.mergeChild() 324 | } 325 | t.size -= subTreeSize 326 | return subTreeSize 327 | } 328 | 329 | // Look for an edge 330 | label := prefix[0] 331 | child := n.getEdge(label) 332 | if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) { 333 | return 0 334 | } 335 | 336 | // Consume the search prefix 337 | if len(child.prefix) > len(prefix) { 338 | prefix = prefix[len(prefix):] 339 | } else { 340 | prefix = prefix[len(child.prefix):] 341 | } 342 | return t.deletePrefix(n, child, prefix) 343 | } 344 | 345 | func (n *node) mergeChild() { 346 | e := n.edges[0] 347 | child := e.node 348 | n.prefix = n.prefix + child.prefix 349 | n.leaf = child.leaf 350 | n.edges = child.edges 351 | } 352 | 353 | // Get is used to lookup a specific key, returning 354 | // the value and if it was found 355 | func (t *Tree) Get(s string) (interface{}, bool) { 356 | n := t.root 357 | search := s 358 | for { 359 | // Check for key exhaution 360 | if len(search) == 0 { 361 | if n.isLeaf() { 362 | return n.leaf.val, true 363 | } 364 | break 365 | } 366 | 367 | // Look for an edge 368 | n = n.getEdge(search[0]) 369 | if n == nil { 370 | break 371 | } 372 | 373 | // Consume the search prefix 374 | if strings.HasPrefix(search, n.prefix) { 375 | search = search[len(n.prefix):] 376 | } else { 377 | break 378 | } 379 | } 380 | return nil, false 381 | } 382 | 383 | // LongestPrefix is like Get, but instead of an 384 | // exact match, it will return the longest prefix match. 385 | func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { 386 | var last *leafNode 387 | n := t.root 388 | search := s 389 | for { 390 | // Look for a leaf node 391 | if n.isLeaf() { 392 | last = n.leaf 393 | } 394 | 395 | // Check for key exhaution 396 | if len(search) == 0 { 397 | break 398 | } 399 | 400 | // Look for an edge 401 | n = n.getEdge(search[0]) 402 | if n == nil { 403 | break 404 | } 405 | 406 | // Consume the search prefix 407 | if strings.HasPrefix(search, n.prefix) { 408 | search = search[len(n.prefix):] 409 | } else { 410 | break 411 | } 412 | } 413 | if last != nil { 414 | return last.key, last.val, true 415 | } 416 | return "", nil, false 417 | } 418 | 419 | // Minimum is used to return the minimum value in the tree 420 | func (t *Tree) Minimum() (string, interface{}, bool) { 421 | n := t.root 422 | for { 423 | if n.isLeaf() { 424 | return n.leaf.key, n.leaf.val, true 425 | } 426 | if len(n.edges) > 0 { 427 | n = n.edges[0].node 428 | } else { 429 | break 430 | } 431 | } 432 | return "", nil, false 433 | } 434 | 435 | // Maximum is used to return the maximum value in the tree 436 | func (t *Tree) Maximum() (string, interface{}, bool) { 437 | n := t.root 438 | for { 439 | if num := len(n.edges); num > 0 { 440 | n = n.edges[num-1].node 441 | continue 442 | } 443 | if n.isLeaf() { 444 | return n.leaf.key, n.leaf.val, true 445 | } 446 | break 447 | } 448 | return "", nil, false 449 | } 450 | 451 | // Walk is used to walk the tree 452 | func (t *Tree) Walk(fn WalkFn) { 453 | recursiveWalk(t.root, fn) 454 | } 455 | 456 | // WalkPrefix is used to walk the tree under a prefix 457 | func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { 458 | n := t.root 459 | search := prefix 460 | for { 461 | // Check for key exhaustion 462 | if len(search) == 0 { 463 | recursiveWalk(n, fn) 464 | return 465 | } 466 | 467 | // Look for an edge 468 | n = n.getEdge(search[0]) 469 | if n == nil { 470 | return 471 | } 472 | 473 | // Consume the search prefix 474 | if strings.HasPrefix(search, n.prefix) { 475 | search = search[len(n.prefix):] 476 | continue 477 | } 478 | if strings.HasPrefix(n.prefix, search) { 479 | // Child may be under our search prefix 480 | recursiveWalk(n, fn) 481 | } 482 | return 483 | } 484 | } 485 | 486 | // WalkPath is used to walk the tree, but only visiting nodes 487 | // from the root down to a given leaf. Where WalkPrefix walks 488 | // all the entries *under* the given prefix, this walks the 489 | // entries *above* the given prefix. 490 | func (t *Tree) WalkPath(path string, fn WalkFn) { 491 | n := t.root 492 | search := path 493 | for { 494 | // Visit the leaf values if any 495 | if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { 496 | return 497 | } 498 | 499 | // Check for key exhaution 500 | if len(search) == 0 { 501 | return 502 | } 503 | 504 | // Look for an edge 505 | n = n.getEdge(search[0]) 506 | if n == nil { 507 | return 508 | } 509 | 510 | // Consume the search prefix 511 | if strings.HasPrefix(search, n.prefix) { 512 | search = search[len(n.prefix):] 513 | } else { 514 | break 515 | } 516 | } 517 | } 518 | 519 | // recursiveWalk is used to do a pre-order walk of a node 520 | // recursively. Returns true if the walk should be aborted 521 | func recursiveWalk(n *node, fn WalkFn) bool { 522 | // Visit the leaf values if any 523 | if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { 524 | return true 525 | } 526 | 527 | // Recurse on the children 528 | i := 0 529 | k := len(n.edges) // keeps track of number of edges in previous iteration 530 | for i < k { 531 | e := n.edges[i] 532 | if recursiveWalk(e.node, fn) { 533 | return true 534 | } 535 | // It is a possibility that the WalkFn modified the node we are 536 | // iterating on. If there are no more edges, mergeChild happened, 537 | // so the last edge became the current node n, on which we'll 538 | // iterate one last time. 539 | if len(n.edges) == 0 { 540 | return recursiveWalk(n, fn) 541 | } 542 | // If there are now less edges than in the previous iteration, 543 | // then do not increment the loop index, since the current index 544 | // points to a new edge. Otherwise, get to the next index. 545 | if len(n.edges) >= k { 546 | i++ 547 | } 548 | k = len(n.edges) 549 | } 550 | return false 551 | } 552 | 553 | // ToMap is used to walk the tree and convert it into a map 554 | func (t *Tree) ToMap() map[string]interface{} { 555 | out := make(map[string]interface{}, t.size) 556 | t.Walk(func(k string, v interface{}) bool { 557 | out[k] = v 558 | return false 559 | }) 560 | return out 561 | } 562 | --------------------------------------------------------------------------------