pax_global_header00006660000000000000000000000064133376720300014516gustar00rootroot0000000000000052 comment=1a2de0c21c94309923825da3df33a4381872c795 golang-github-armon-go-radix-1.0.0/000077500000000000000000000000001333767203000170655ustar00rootroot00000000000000golang-github-armon-go-radix-1.0.0/.gitignore000066400000000000000000000003741333767203000210610ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe golang-github-armon-go-radix-1.0.0/.travis.yml000066400000000000000000000000311333767203000211700ustar00rootroot00000000000000language: go go: - tip golang-github-armon-go-radix-1.0.0/LICENSE000066400000000000000000000020671333767203000200770ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Armon Dadgar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-armon-go-radix-1.0.0/README.md000066400000000000000000000020621333767203000203440ustar00rootroot00000000000000go-radix [![Build Status](https://travis-ci.org/armon/go-radix.png)](https://travis-ci.org/armon/go-radix) ========= Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree). The package only provides a single `Tree` implementation, optimized for sparse nodes. As a radix tree, it provides the following: * O(k) operations. In many cases, this can be faster than a hash table since the hash function is an O(k) operation, and hash tables have very poor cache locality. * Minimum / Maximum value lookups * Ordered iteration For an immutable variant, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix). Documentation ============= The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix). Example ======= Below is a simple example of usage ```go // Create a tree r := radix.New() r.Insert("foo", 1) r.Insert("bar", 2) r.Insert("foobar", 2) // Find the longest prefix match m, _, _ := r.LongestPrefix("foozip") if m != "foo" { panic("should be foo") } ``` golang-github-armon-go-radix-1.0.0/go.mod000066400000000000000000000000411333767203000201660ustar00rootroot00000000000000module github.com/armon/go-radix golang-github-armon-go-radix-1.0.0/radix.go000066400000000000000000000246671333767203000205420ustar00rootroot00000000000000package radix import ( "sort" "strings" ) // WalkFn is used when walking the tree. Takes a // key and value, returning if iteration should // be terminated. type WalkFn func(s string, v interface{}) bool // leafNode is used to represent a value type leafNode struct { key string val interface{} } // edge is used to represent an edge node type edge struct { label byte node *node } type node struct { // leaf is used to store possible leaf leaf *leafNode // prefix is the common prefix we ignore prefix string // Edges should be stored in-order for iteration. // We avoid a fully materialized slice to save memory, // since in most cases we expect to be sparse edges edges } func (n *node) isLeaf() bool { return n.leaf != nil } func (n *node) addEdge(e edge) { n.edges = append(n.edges, e) n.edges.Sort() } func (n *node) updateEdge(label byte, node *node) { num := len(n.edges) idx := sort.Search(num, func(i int) bool { return n.edges[i].label >= label }) if idx < num && n.edges[idx].label == label { n.edges[idx].node = node return } panic("replacing missing edge") } func (n *node) getEdge(label byte) *node { num := len(n.edges) idx := sort.Search(num, func(i int) bool { return n.edges[i].label >= label }) if idx < num && n.edges[idx].label == label { return n.edges[idx].node } return nil } func (n *node) delEdge(label byte) { num := len(n.edges) idx := sort.Search(num, func(i int) bool { return n.edges[i].label >= label }) if idx < num && n.edges[idx].label == label { copy(n.edges[idx:], n.edges[idx+1:]) n.edges[len(n.edges)-1] = edge{} n.edges = n.edges[:len(n.edges)-1] } } type edges []edge func (e edges) Len() int { return len(e) } func (e edges) Less(i, j int) bool { return e[i].label < e[j].label } func (e edges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } func (e edges) Sort() { sort.Sort(e) } // Tree implements a radix tree. This can be treated as a // Dictionary abstract data type. The main advantage over // a standard hash map is prefix-based lookups and // ordered iteration, type Tree struct { root *node size int } // New returns an empty Tree func New() *Tree { return NewFromMap(nil) } // NewFromMap returns a new tree containing the keys // from an existing map func NewFromMap(m map[string]interface{}) *Tree { t := &Tree{root: &node{}} for k, v := range m { t.Insert(k, v) } return t } // Len is used to return the number of elements in the tree func (t *Tree) Len() int { return t.size } // longestPrefix finds the length of the shared prefix // of two strings func longestPrefix(k1, k2 string) int { max := len(k1) if l := len(k2); l < max { max = l } var i int for i = 0; i < max; i++ { if k1[i] != k2[i] { break } } return i } // Insert is used to add a newentry or update // an existing entry. Returns if updated. func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { var parent *node n := t.root search := s for { // Handle key exhaution if len(search) == 0 { if n.isLeaf() { old := n.leaf.val n.leaf.val = v return old, true } n.leaf = &leafNode{ key: s, val: v, } t.size++ return nil, false } // Look for the edge parent = n n = n.getEdge(search[0]) // No edge, create one if n == nil { e := edge{ label: search[0], node: &node{ leaf: &leafNode{ key: s, val: v, }, prefix: search, }, } parent.addEdge(e) t.size++ return nil, false } // Determine longest prefix of the search key on match commonPrefix := longestPrefix(search, n.prefix) if commonPrefix == len(n.prefix) { search = search[commonPrefix:] continue } // Split the node t.size++ child := &node{ prefix: search[:commonPrefix], } parent.updateEdge(search[0], child) // Restore the existing node child.addEdge(edge{ label: n.prefix[commonPrefix], node: n, }) n.prefix = n.prefix[commonPrefix:] // Create a new leaf node leaf := &leafNode{ key: s, val: v, } // If the new key is a subset, add to to this node search = search[commonPrefix:] if len(search) == 0 { child.leaf = leaf return nil, false } // Create a new edge for the node child.addEdge(edge{ label: search[0], node: &node{ leaf: leaf, prefix: search, }, }) return nil, false } } // Delete is used to delete a key, returning the previous // value and if it was deleted func (t *Tree) Delete(s string) (interface{}, bool) { var parent *node var label byte n := t.root search := s for { // Check for key exhaution if len(search) == 0 { if !n.isLeaf() { break } goto DELETE } // Look for an edge parent = n label = search[0] n = n.getEdge(label) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } return nil, false DELETE: // Delete the leaf leaf := n.leaf n.leaf = nil t.size-- // Check if we should delete this node from the parent if parent != nil && len(n.edges) == 0 { parent.delEdge(label) } // Check if we should merge this node if n != t.root && len(n.edges) == 1 { n.mergeChild() } // Check if we should merge the parent's other child if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { parent.mergeChild() } return leaf.val, true } // DeletePrefix is used to delete the subtree under a prefix // Returns how many nodes were deleted // Use this to delete large subtrees efficiently func (t *Tree) DeletePrefix(s string) int { return t.deletePrefix(nil, t.root, s) } // delete does a recursive deletion func (t *Tree) deletePrefix(parent, n *node, prefix string) int { // Check for key exhaustion if len(prefix) == 0 { // Remove the leaf node subTreeSize := 0 //recursively walk from all edges of the node to be deleted recursiveWalk(n, func(s string, v interface{}) bool { subTreeSize++ return false }) if n.isLeaf() { n.leaf = nil } n.edges = nil // deletes the entire subtree // Check if we should merge the parent's other child if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { parent.mergeChild() } t.size -= subTreeSize return subTreeSize } // Look for an edge label := prefix[0] child := n.getEdge(label) if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) { return 0 } // Consume the search prefix if len(child.prefix) > len(prefix) { prefix = prefix[len(prefix):] } else { prefix = prefix[len(child.prefix):] } return t.deletePrefix(n, child, prefix) } func (n *node) mergeChild() { e := n.edges[0] child := e.node n.prefix = n.prefix + child.prefix n.leaf = child.leaf n.edges = child.edges } // Get is used to lookup a specific key, returning // the value and if it was found func (t *Tree) Get(s string) (interface{}, bool) { n := t.root search := s for { // Check for key exhaution if len(search) == 0 { if n.isLeaf() { return n.leaf.val, true } break } // Look for an edge n = n.getEdge(search[0]) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } return nil, false } // LongestPrefix is like Get, but instead of an // exact match, it will return the longest prefix match. func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { var last *leafNode n := t.root search := s for { // Look for a leaf node if n.isLeaf() { last = n.leaf } // Check for key exhaution if len(search) == 0 { break } // Look for an edge n = n.getEdge(search[0]) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } if last != nil { return last.key, last.val, true } return "", nil, false } // Minimum is used to return the minimum value in the tree func (t *Tree) Minimum() (string, interface{}, bool) { n := t.root for { if n.isLeaf() { return n.leaf.key, n.leaf.val, true } if len(n.edges) > 0 { n = n.edges[0].node } else { break } } return "", nil, false } // Maximum is used to return the maximum value in the tree func (t *Tree) Maximum() (string, interface{}, bool) { n := t.root for { if num := len(n.edges); num > 0 { n = n.edges[num-1].node continue } if n.isLeaf() { return n.leaf.key, n.leaf.val, true } break } return "", nil, false } // Walk is used to walk the tree func (t *Tree) Walk(fn WalkFn) { recursiveWalk(t.root, fn) } // WalkPrefix is used to walk the tree under a prefix func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { n := t.root search := prefix for { // Check for key exhaution if len(search) == 0 { recursiveWalk(n, fn) return } // Look for an edge n = n.getEdge(search[0]) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else if strings.HasPrefix(n.prefix, search) { // Child may be under our search prefix recursiveWalk(n, fn) return } else { break } } } // WalkPath is used to walk the tree, but only visiting nodes // from the root down to a given leaf. Where WalkPrefix walks // all the entries *under* the given prefix, this walks the // entries *above* the given prefix. func (t *Tree) WalkPath(path string, fn WalkFn) { n := t.root search := path for { // Visit the leaf values if any if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { return } // Check for key exhaution if len(search) == 0 { return } // Look for an edge n = n.getEdge(search[0]) if n == nil { return } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } } // recursiveWalk is used to do a pre-order walk of a node // recursively. Returns true if the walk should be aborted func recursiveWalk(n *node, fn WalkFn) bool { // Visit the leaf values if any if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { return true } // Recurse on the children for _, e := range n.edges { if recursiveWalk(e.node, fn) { return true } } return false } // ToMap is used to walk the tree and convert it into a map func (t *Tree) ToMap() map[string]interface{} { out := make(map[string]interface{}, t.size) t.Walk(func(k string, v interface{}) bool { out[k] = v return false }) return out } golang-github-armon-go-radix-1.0.0/radix_test.go000066400000000000000000000141071333767203000215650ustar00rootroot00000000000000package radix import ( crand "crypto/rand" "fmt" "reflect" "sort" "testing" ) func TestRadix(t *testing.T) { var min, max string inp := make(map[string]interface{}) for i := 0; i < 1000; i++ { gen := generateUUID() inp[gen] = i if gen < min || i == 0 { min = gen } if gen > max || i == 0 { max = gen } } r := NewFromMap(inp) if r.Len() != len(inp) { t.Fatalf("bad length: %v %v", r.Len(), len(inp)) } r.Walk(func(k string, v interface{}) bool { println(k) return false }) for k, v := range inp { out, ok := r.Get(k) if !ok { t.Fatalf("missing key: %v", k) } if out != v { t.Fatalf("value mis-match: %v %v", out, v) } } // Check min and max outMin, _, _ := r.Minimum() if outMin != min { t.Fatalf("bad minimum: %v %v", outMin, min) } outMax, _, _ := r.Maximum() if outMax != max { t.Fatalf("bad maximum: %v %v", outMax, max) } for k, v := range inp { out, ok := r.Delete(k) if !ok { t.Fatalf("missing key: %v", k) } if out != v { t.Fatalf("value mis-match: %v %v", out, v) } } if r.Len() != 0 { t.Fatalf("bad length: %v", r.Len()) } } func TestRoot(t *testing.T) { r := New() _, ok := r.Delete("") if ok { t.Fatalf("bad") } _, ok = r.Insert("", true) if ok { t.Fatalf("bad") } val, ok := r.Get("") if !ok || val != true { t.Fatalf("bad: %v", val) } val, ok = r.Delete("") if !ok || val != true { t.Fatalf("bad: %v", val) } } func TestDelete(t *testing.T) { r := New() s := []string{"", "A", "AB"} for _, ss := range s { r.Insert(ss, true) } for _, ss := range s { _, ok := r.Delete(ss) if !ok { t.Fatalf("bad %q", ss) } } } func TestDeletePrefix(t *testing.T) { type exp struct { inp []string prefix string out []string numDeleted int } cases := []exp{ {[]string{"", "A", "AB", "ABC", "R", "S"}, "A", []string{"", "R", "S"}, 3}, {[]string{"", "A", "AB", "ABC", "R", "S"}, "ABC", []string{"", "A", "AB", "R", "S"}, 1}, {[]string{"", "A", "AB", "ABC", "R", "S"}, "", []string{}, 6}, {[]string{"", "A", "AB", "ABC", "R", "S"}, "S", []string{"", "A", "AB", "ABC", "R"}, 1}, {[]string{"", "A", "AB", "ABC", "R", "S"}, "SS", []string{"", "A", "AB", "ABC", "R", "S"}, 0}, } for _, test := range cases { r := New() for _, ss := range test.inp { r.Insert(ss, true) } deleted := r.DeletePrefix(test.prefix) if deleted != test.numDeleted { t.Fatalf("Bad delete, expected %v to be deleted but got %v", test.numDeleted, deleted) } out := []string{} fn := func(s string, v interface{}) bool { out = append(out, s) return false } r.Walk(fn) if !reflect.DeepEqual(out, test.out) { t.Fatalf("mis-match: %v %v", out, test.out) } } } func TestLongestPrefix(t *testing.T) { r := New() keys := []string{ "", "foo", "foobar", "foobarbaz", "foobarbazzip", "foozip", } for _, k := range keys { r.Insert(k, nil) } if r.Len() != len(keys) { t.Fatalf("bad len: %v %v", r.Len(), len(keys)) } type exp struct { inp string out string } cases := []exp{ {"a", ""}, {"abc", ""}, {"fo", ""}, {"foo", "foo"}, {"foob", "foo"}, {"foobar", "foobar"}, {"foobarba", "foobar"}, {"foobarbaz", "foobarbaz"}, {"foobarbazzi", "foobarbaz"}, {"foobarbazzip", "foobarbazzip"}, {"foozi", "foo"}, {"foozip", "foozip"}, {"foozipzap", "foozip"}, } for _, test := range cases { m, _, ok := r.LongestPrefix(test.inp) if !ok { t.Fatalf("no match: %v", test) } if m != test.out { t.Fatalf("mis-match: %v %v", m, test) } } } func TestWalkPrefix(t *testing.T) { r := New() keys := []string{ "foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap", "zipzap", } for _, k := range keys { r.Insert(k, nil) } if r.Len() != len(keys) { t.Fatalf("bad len: %v %v", r.Len(), len(keys)) } type exp struct { inp string out []string } cases := []exp{ { "f", []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, }, { "foo", []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, }, { "foob", []string{"foobar"}, }, { "foo/", []string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"}, }, { "foo/b", []string{"foo/bar/baz", "foo/baz/bar"}, }, { "foo/ba", []string{"foo/bar/baz", "foo/baz/bar"}, }, { "foo/bar", []string{"foo/bar/baz"}, }, { "foo/bar/baz", []string{"foo/bar/baz"}, }, { "foo/bar/bazoo", []string{}, }, { "z", []string{"zipzap"}, }, } for _, test := range cases { out := []string{} fn := func(s string, v interface{}) bool { out = append(out, s) return false } r.WalkPrefix(test.inp, fn) sort.Strings(out) sort.Strings(test.out) if !reflect.DeepEqual(out, test.out) { t.Fatalf("mis-match: %v %v", out, test.out) } } } func TestWalkPath(t *testing.T) { r := New() keys := []string{ "foo", "foo/bar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap", "zipzap", } for _, k := range keys { r.Insert(k, nil) } if r.Len() != len(keys) { t.Fatalf("bad len: %v %v", r.Len(), len(keys)) } type exp struct { inp string out []string } cases := []exp{ { "f", []string{}, }, { "foo", []string{"foo"}, }, { "foo/", []string{"foo"}, }, { "foo/ba", []string{"foo"}, }, { "foo/bar", []string{"foo", "foo/bar"}, }, { "foo/bar/baz", []string{"foo", "foo/bar", "foo/bar/baz"}, }, { "foo/bar/bazoo", []string{"foo", "foo/bar", "foo/bar/baz"}, }, { "z", []string{}, }, } for _, test := range cases { out := []string{} fn := func(s string, v interface{}) bool { out = append(out, s) return false } r.WalkPath(test.inp, fn) sort.Strings(out) sort.Strings(test.out) if !reflect.DeepEqual(out, test.out) { t.Fatalf("mis-match: %v %v", out, test.out) } } } // generateUUID is used to generate a random UUID func generateUUID() string { buf := make([]byte, 16) if _, err := crand.Read(buf); err != nil { panic(fmt.Errorf("failed to read random bytes: %v", err)) } return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", buf[0:4], buf[4:6], buf[6:8], buf[8:10], buf[10:16]) }