pax_global_header00006660000000000000000000000064135571523670014530gustar00rootroot0000000000000052 comment=4536a49cdb164af1fc76e276ebeaf004338d2044 critbitgo-1.4.0/000077500000000000000000000000001355715236700135205ustar00rootroot00000000000000critbitgo-1.4.0/.travis.yml000066400000000000000000000000601355715236700156250ustar00rootroot00000000000000language: go go: - 1.5 - 1.6 - 1.7 critbitgo-1.4.0/CHANGES.md000066400000000000000000000011761355715236700151170ustar00rootroot00000000000000## 1.4.0 (2019/11/02) - Add Net.WalkPrefix [#10](https://github.com/k-sone/critbitgo/pull/10) - Add Net.WalkMatch [#11](https://github.com/k-sone/critbitgo/pull/11) - Fix Allprefixed [#12](https://github.com/k-sone/critbitgo/pull/12) - Make Walk API handle empty trie [#13](https://github.com/k-sone/critbitgo/pull/13) ## 1.3.0 (2019/09/30) - Add Net.Walk [#9](https://github.com/k-sone/critbitgo/pull/9) ## 1.2.0 (2018/04/25) - Add ContainedIP() as fast way to check an IP [#7](https://github.com/k-sone/critbitgo/pull/7) ## 1.1.0 (2016/12/29) - Add `LongestPrefix ` and `Walk` functions ## 1.0.0 (2016/04/02) - Initial release critbitgo-1.4.0/LICENSE000066400000000000000000000020661355715236700145310ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Keita Sone 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. critbitgo-1.4.0/README.md000066400000000000000000000020531355715236700147770ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/k-sone/critbitgo.svg?branch=master)](https://travis-ci.org/k-sone/critbitgo) critbitgo ========= [Crit-bit trees](http://cr.yp.to/critbit.html) in golang and its applications. This implementation extended to handle the key that contains a null character from [C implementation](https://github.com/agl/critbit). Usage -------- ```go // Create Trie trie := critbitgo.NewTrie() // Insert trie.Insert([]byte("aa"), "value1") trie.Insert([]byte("bb"), "value2") trie.Insert([]byte("ab"), "value3") // Get v, ok := trie.Get([]byte("aa")) fmt.Println(v, ok) // -> value1 true // Iterate containing keys trie.Allprefixed([]byte{}, func(key []byte, value interface{}) bool { fmt.Println(key, value) // -> [97 97] value1 // [97 98] value3 // [98 98] value2 return true }) // Delete v, ok = trie.Delete([]byte("aa")) fmt.Println(v, ok) // -> value1 true v, ok = trie.Delete([]byte("aa")) fmt.Println(v, ok) // -> false ``` License ------- MIT critbitgo-1.4.0/critbit.go000066400000000000000000000201001355715236700155000ustar00rootroot00000000000000package critbitgo import ( "bytes" "encoding/hex" "fmt" "io" "os" "strconv" ) // The matrix of most significant bit var msbMatrix [256]byte func buildMsbMatrix() { for i := 0; i < len(msbMatrix); i++ { b := byte(i) b |= b >> 1 b |= b >> 2 b |= b >> 4 msbMatrix[i] = b &^ (b >> 1) } } type node struct { internal *internal external *external } type internal struct { child [2]node offset int bit byte cont bool // if true, key of child[1] contains key of child[0] } type external struct { key []byte value interface{} } // finding the critical bit. func (n *external) criticalBit(key []byte) (offset int, bit byte, cont bool) { nlen := len(n.key) klen := len(key) mlen := nlen if nlen > klen { mlen = klen } // find first differing byte and bit for offset = 0; offset < mlen; offset++ { if a, b := key[offset], n.key[offset]; a != b { bit = msbMatrix[a^b] return } } if nlen < klen { bit = msbMatrix[key[offset]] } else if nlen > klen { bit = msbMatrix[n.key[offset]] } else { // two keys are equal offset = -1 } return offset, bit, true } // calculate direction. func (n *internal) direction(key []byte) int { if n.offset < len(key) && (key[n.offset]&n.bit != 0 || n.cont) { return 1 } return 0 } // Crit-bit Tree type Trie struct { root node size int } // searching the tree. func (t *Trie) search(key []byte) *node { n := &t.root for n.internal != nil { n = &n.internal.child[n.internal.direction(key)] } return n } // membership testing. func (t *Trie) Contains(key []byte) bool { if n := t.search(key); n.external != nil && bytes.Equal(n.external.key, key) { return true } return false } // get member. // if `key` is in Trie, `ok` is true. func (t *Trie) Get(key []byte) (value interface{}, ok bool) { if n := t.search(key); n.external != nil && bytes.Equal(n.external.key, key) { return n.external.value, true } return } // insert into the tree (replaceable). func (t *Trie) insert(key []byte, value interface{}, replace bool) bool { // an empty tree if t.size == 0 { t.root.external = &external{ key: key, value: value, } t.size = 1 return true } n := t.search(key) newOffset, newBit, newCont := n.external.criticalBit(key) // already exists in the tree if newOffset == -1 { if replace { n.external.value = value return true } return false } // allocate new node newNode := &internal{ offset: newOffset, bit: newBit, cont: newCont, } direction := newNode.direction(key) newNode.child[direction].external = &external{ key: key, value: value, } // insert new node wherep := &t.root for in := wherep.internal; in != nil; in = wherep.internal { if in.offset > newOffset || (in.offset == newOffset && in.bit < newBit) { break } wherep = &in.child[in.direction(key)] } if wherep.internal != nil { newNode.child[1-direction].internal = wherep.internal } else { newNode.child[1-direction].external = wherep.external wherep.external = nil } wherep.internal = newNode t.size += 1 return true } // insert into the tree. // if `key` is alredy in Trie, return false. func (t *Trie) Insert(key []byte, value interface{}) bool { return t.insert(key, value, false) } // set into the tree. func (t *Trie) Set(key []byte, value interface{}) { t.insert(key, value, true) } // deleting elements. // if `key` is in Trie, `ok` is true. func (t *Trie) Delete(key []byte) (value interface{}, ok bool) { // an empty tree if t.size == 0 { return } var direction int var whereq *node // pointer to the grandparent var wherep *node = &t.root // finding the best candidate to delete for in := wherep.internal; in != nil; in = wherep.internal { direction = in.direction(key) whereq = wherep wherep = &in.child[direction] } // checking that we have the right element if !bytes.Equal(wherep.external.key, key) { return } value = wherep.external.value ok = true // removing the node if whereq == nil { wherep.external = nil } else { othern := whereq.internal.child[1-direction] whereq.internal = othern.internal whereq.external = othern.external } t.size -= 1 return } // clearing a tree. func (t *Trie) Clear() { t.root.internal = nil t.root.external = nil t.size = 0 } // return the number of key in a tree. func (t *Trie) Size() int { return t.size } // fetching elements with a given prefix. // handle is called with arguments key and value (if handle returns `false`, the iteration is aborted) func (t *Trie) Allprefixed(prefix []byte, handle func(key []byte, value interface{}) bool) bool { // an empty tree if t.size == 0 { return true } // walk tree, maintaining top pointer p := &t.root top := p if len(prefix) > 0 { for q := p.internal; q != nil; q = p.internal { p = &q.child[q.direction(prefix)] if q.offset < len(prefix) { top = p } } // check prefix if !bytes.HasPrefix(p.external.key, prefix) { return true } } return allprefixed(top, handle) } func allprefixed(n *node, handle func([]byte, interface{}) bool) bool { if n.internal != nil { // dealing with an internal node while recursing for i := 0; i < 2; i++ { if !allprefixed(&n.internal.child[i], handle) { return false } } } else { // dealing with an external node while recursing return handle(n.external.key, n.external.value) } return true } // Search for the longest matching key from the beginning of the given key. // if `key` is in Trie, `ok` is true. func (t *Trie) LongestPrefix(given []byte) (key []byte, value interface{}, ok bool) { // an empty tree if t.size == 0 { return } return longestPrefix(&t.root, given) } func longestPrefix(n *node, key []byte) ([]byte, interface{}, bool) { if n.internal != nil { direction := n.internal.direction(key) if k, v, ok := longestPrefix(&n.internal.child[direction], key); ok { return k, v, ok } if direction == 1 { return longestPrefix(&n.internal.child[0], key) } } else { if bytes.HasPrefix(key, n.external.key) { return n.external.key, n.external.value, true } } return nil, nil, false } // Iterating elements from a given start key. // handle is called with arguments key and value (if handle returns `false`, the iteration is aborted) func (t *Trie) Walk(start []byte, handle func(key []byte, value interface{}) bool) bool { if t.size == 0 { return true } var seek bool if start != nil { seek = true } return walk(&t.root, start, &seek, handle) } func walk(n *node, key []byte, seek *bool, handle func([]byte, interface{}) bool) bool { if n.internal != nil { var direction int if *seek { direction = n.internal.direction(key) } if !walk(&n.internal.child[direction], key, seek, handle) { return false } if !(*seek) && direction == 0 { // iteration another side return walk(&n.internal.child[1], key, seek, handle) } return true } else { if *seek { if bytes.Equal(n.external.key, key) { // seek completed *seek = false } else { // key is not in Trie return false } } return handle(n.external.key, n.external.value) } } // dump tree. (for debugging) func (t *Trie) Dump(w io.Writer) { if t.root.internal == nil && t.root.external == nil { return } if w == nil { w = os.Stdout } dump(w, &t.root, true, "") } func dump(w io.Writer, n *node, right bool, prefix string) { var ownprefix string if right { ownprefix = prefix } else { ownprefix = prefix[:len(prefix)-1] + "`" } if in := n.internal; in != nil { fmt.Fprintf(w, "%s-- off=%d, bit=%08b(%02x), cont=%v\n", ownprefix, in.offset, in.bit, in.bit, in.cont) for i := 0; i < 2; i++ { var nextprefix string switch i { case 0: nextprefix = prefix + " |" right = true case 1: nextprefix = prefix + " " right = false } dump(w, &in.child[i], right, nextprefix) } } else { fmt.Fprintf(w, "%s-- key=%d (%s)\n", ownprefix, n.external.key, key2str(n.external.key)) } return } func key2str(key []byte) string { for _, c := range key { if !strconv.IsPrint(rune(c)) { return hex.EncodeToString(key) } } return string(key) } // create a tree. func NewTrie() *Trie { return &Trie{} } func init() { buildMsbMatrix() } critbitgo-1.4.0/critbit_test.go000066400000000000000000000231331355715236700165500ustar00rootroot00000000000000package critbitgo_test import ( "bytes" "math/rand" "testing" "github.com/k-sone/critbitgo" ) func buildTrie(t *testing.T, keys []string) *critbitgo.Trie { trie := critbitgo.NewTrie() for _, key := range keys { if !trie.Insert([]byte(key), key) { t.Errorf("Insert() - failed insert \"%s\"\n%s", key, dumpTrie(trie)) } } return trie } func dumpTrie(trie *critbitgo.Trie) string { buf := bytes.NewBufferString("") trie.Dump(buf) return buf.String() } func TestInsert(t *testing.T) { // normal build keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) dump := dumpTrie(trie) // random build random := rand.New(rand.NewSource(0)) for i := 0; i < 10; i++ { // shuffle keys lkeys := make([]string, len(keys)) for j, index := range random.Perm(len(keys)) { lkeys[j] = keys[index] } ltrie := buildTrie(t, lkeys) ldump := dumpTrie(ltrie) if dump != ldump { t.Errorf("Insert() - different tries\norigin:\n%s\nother:\n%s\n", dump, ldump) } } // error check if trie.Insert([]byte("a"), nil) { t.Error("Insert() - check exists") } if !trie.Insert([]byte("c"), nil) { t.Error("Insert() - check not exists") } } func TestSet(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) trie.Set([]byte("a"), 100) v, _ := trie.Get([]byte("a")) if n, ok := v.(int); !ok || n != 100 { t.Errorf("Set() - failed replace - %v", v) } } func TestContains(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) for _, key := range keys { if !trie.Contains([]byte(key)) { t.Errorf("Contains() - not found - %s", key) } } if trie.Contains([]byte("aaa")) { t.Error("Contains() - phantom found") } } func TestGet(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) for _, key := range keys { if value, ok := trie.Get([]byte(key)); value != key || !ok { t.Errorf("Get() - not found - %s", key) } } if value, ok := trie.Get([]byte("aaa")); value != nil || ok { t.Error("Get() - phantom found") } } func TestDelete(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) for i, key := range keys { if !trie.Contains([]byte(key)) { t.Errorf("Delete() - not exists - %s", key) } if v, ok := trie.Delete([]byte(key)); !ok || v != key { t.Errorf("Delete() - failed - %s", key) } if trie.Contains([]byte(key)) { t.Errorf("Delete() - exists - %s", key) } if i != len(keys) { for _, key2 := range keys[i+1:] { if !trie.Contains([]byte(key2)) { t.Errorf("Delete() - other not exists - %s", key2) } } } } } func TestSize(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) klen := len(keys) if s := trie.Size(); s != klen { t.Errorf("Size() - expected [%d], actual [%d]", klen, s) } for i, key := range keys { trie.Delete([]byte(key)) if s := trie.Size(); s != klen-(i+1) { t.Errorf("Size() - expected [%d], actual [%d]", klen, s) } } } func TestAllprefixed(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab", "bc"} trie := buildTrie(t, keys) elems := make([]string, 0, len(keys)) handle := func(key []byte, value interface{}) bool { if k := string(key); k == value { elems = append(elems, k) } return true } if !trie.Allprefixed([]byte{}, handle) { t.Error("Allprefixed() - invalid result") } if len(elems) != 10 { t.Errorf("Allprefixed() - invalid elems length [%v]", elems) } for i, key := range []string{"", "a", "aa", "ab", "aba", "b", "ba", "bab", "bb", "bc"} { if key != elems[i] { t.Errorf("Allprefixed() - not found [%s]", key) } } elems = make([]string, 0, len(keys)) if !trie.Allprefixed([]byte("a"), handle) { t.Error("Allprefixed() - invalid result") } if len(elems) != 4 { t.Errorf("Allprefixed() - invalid elems length [%v]", elems) } for i, key := range []string{"a", "aa", "ab", "aba"} { if key != elems[i] { t.Errorf("Allprefixed() - not found [%s]", key) } } elems = make([]string, 0, len(keys)) if !trie.Allprefixed([]byte("bb"), handle) { t.Error("Allprefixed() - invalid result") } if len(elems) != 1 { t.Errorf("Allprefixed() - invalid elems length [%v]", elems) } for i, key := range []string{"bb"} { if key != elems[i] { t.Errorf("Allprefixed() - not found [%s]", key) } } elems = make([]string, 0, len(keys)) handle = func(key []byte, value interface{}) bool { if k := string(key); k == value { elems = append(elems, k) } if string(key) == "aa" { return false } return true } if trie.Allprefixed([]byte("a"), handle) { t.Error("Allprefixed() - invalid result") } if len(elems) != 2 { t.Errorf("Allprefixed() - invalid elems length [%v]", elems) } for i, key := range []string{"a", "aa"} { if key != elems[i] { t.Errorf("Allprefixed() - not found [%s]", key) } } keys = []string{"bad", "badbad"} trie = buildTrie(t, keys) elems = make([]string, 0, len(keys)) if !trie.Allprefixed([]byte("ad"), handle) { t.Error("Allprefixed() - invalid result") } if len(elems) != 0 { t.Errorf("Allprefixed() - invalid elems length [%v]", elems) } } func TestLongestPrefix(t *testing.T) { keys := []string{"a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) expects := map[string]string{ "a": "a", "a^": "a", "aaa": "aa", "abc": "ab", "bac": "ba", "bbb": "bb", "bc": "b", } for g, k := range expects { if key, value, ok := trie.LongestPrefix([]byte(g)); !ok || string(key) != k || value != k { t.Errorf("LongestPrefix() - invalid result - %s not %s", key, g) } } if _, _, ok := trie.LongestPrefix([]byte{}); ok { t.Error("LongestPrefix() - invalid result - not empty") } if _, _, ok := trie.LongestPrefix([]byte("^")); ok { t.Error("LongestPrefix() - invalid result - not empty") } if _, _, ok := trie.LongestPrefix([]byte("c")); ok { t.Error("LongestPrefix() - invalid result - not empty") } } func TestWalk(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} trie := buildTrie(t, keys) elems := make([]string, 0, len(keys)) handle := func(key []byte, value interface{}) bool { if k := string(key); k == value { elems = append(elems, k) } return true } if !trie.Walk([]byte{}, handle) { t.Error("Walk() - invalid result") } if len(elems) != 9 { t.Errorf("Walk() - invalid elems length [%v]", elems) } for i, key := range []string{"", "a", "aa", "ab", "aba", "b", "ba", "bab", "bb"} { if key != elems[i] { t.Errorf("Walk() - not found [%s]", key) } } elems = make([]string, 0, len(keys)) if !trie.Walk([]byte("ab"), handle) { t.Error("Walk() - invalid result") } if len(elems) != 6 { t.Errorf("Walk() - invalid elems length [%v]", elems) } for i, key := range []string{"ab", "aba", "b", "ba", "bab", "bb"} { if key != elems[i] { t.Errorf("Walk() - not found [%s]", key) } } elems = make([]string, 0, len(keys)) if !trie.Walk(nil, handle) { t.Error("Walk() - invalid result") } if len(elems) != 9 { t.Errorf("Walk() - invalid elems length [%v]", elems) } for i, key := range []string{"", "a", "aa", "ab", "aba", "b", "ba", "bab", "bb"} { if key != elems[i] { t.Errorf("Walk() - not found [%s]", key) } } elems = make([]string, 0, len(keys)) handle = func(key []byte, value interface{}) bool { if k := string(key); k == value { elems = append(elems, k) } if string(key) == "aa" { return false } return true } if trie.Walk([]byte("a"), handle) { t.Error("Walk() - invalid result") } if len(elems) != 2 { t.Errorf("Walk() - invalid elems length [%v]", elems) } for i, key := range []string{"a", "aa"} { if key != elems[i] { t.Errorf("Walk() - not found [%s]", key) } } if trie.Walk([]byte("^"), handle) { t.Error("Walk() - invalid result") } if trie.Walk([]byte("aaa"), handle) { t.Error("Walk() - invalid result") } if trie.Walk([]byte("c"), handle) { t.Error("Walk() - invalid result") } } func TestKeyContainsZeroValue(t *testing.T) { trie := critbitgo.NewTrie() trie.Insert([]byte{1, 0, 1}, nil) trie.Insert([]byte{1}, nil) trie.Insert([]byte{0, 1, 1}, nil) trie.Insert([]byte{}, nil) trie.Insert([]byte{0, 0, 1}, nil) trie.Insert([]byte{1, 1}, nil) trie.Insert([]byte{1, 1, 1}, nil) trie.Insert([]byte{0, 1}, nil) trie.Insert([]byte{0, 1, 0}, nil) trie.Insert([]byte{0, 0}, nil) trie.Insert([]byte{0, 0, 0}, nil) trie.Insert([]byte{0}, nil) var index int exp := [][]byte{ []byte{}, []byte{0}, []byte{0, 0}, []byte{0, 0, 0}, []byte{0, 0, 1}, []byte{0, 1}, []byte{0, 1, 0}, []byte{0, 1, 1}, []byte{1}, []byte{1, 0, 1}, []byte{1, 1}, []byte{1, 1, 1}, } handle := func(key []byte, _ interface{}) bool { if !bytes.Equal(exp[index], key) { t.Errorf("Key Order - index=%d, expected [%x], actula [%x]", index, exp[index], key) } index += 1 return true } trie.Allprefixed([]byte(""), handle) } func TestEmptyTree(t *testing.T) { trie := critbitgo.NewTrie() key := []byte{0, 1, 2} handle := func(_ []byte, _ interface{}) bool { return true } assert := func(n string, f func()) { defer func() { if e := recover(); e != nil { t.Errorf("%s() - empty tree : %v", n, e) } }() f() } assert("Contains", func() { trie.Contains(key) }) assert("Get", func() { trie.Get(key) }) assert("Delete", func() { trie.Delete(key) }) assert("LongestPrefix", func() { trie.LongestPrefix(key) }) assert("Allprefixed", func() { trie.Allprefixed(key, handle) }) assert("Walk", func() { trie.Walk(key, handle) }) } critbitgo-1.4.0/map.go000066400000000000000000000025441355715236700146310ustar00rootroot00000000000000package critbitgo import ( "unsafe" ) // The map is sorted according to the natural ordering of its keys type SortedMap struct { trie *Trie } func (m *SortedMap) Contains(key string) bool { return m.trie.Contains(*(*[]byte)(unsafe.Pointer(&key))) } func (m *SortedMap) Get(key string) (value interface{}, ok bool) { return m.trie.Get(*(*[]byte)(unsafe.Pointer(&key))) } func (m *SortedMap) Set(key string, value interface{}) { m.trie.Set([]byte(key), value) } func (m *SortedMap) Delete(key string) (value interface{}, ok bool) { return m.trie.Delete(*(*[]byte)(unsafe.Pointer(&key))) } func (m *SortedMap) Clear() { m.trie.Clear() } func (m *SortedMap) Size() int { return m.trie.Size() } // Returns a slice of sorted keys func (m *SortedMap) Keys() []string { keys := make([]string, 0, m.Size()) m.trie.Allprefixed([]byte{}, func(k []byte, v interface{}) bool { keys = append(keys, string(k)) return true }) return keys } // Executes a provided function for each element that has a given prefix. // if handle returns `false`, the iteration is aborted. func (m *SortedMap) Each(prefix string, handle func(key string, value interface{}) bool) bool { return m.trie.Allprefixed([]byte(prefix), func(k []byte, v interface{}) bool { return handle(string(k), v) }) } // Create a SortedMap func NewSortedMap() *SortedMap { return &SortedMap{NewTrie()} } critbitgo-1.4.0/map_bench_test.go000066400000000000000000000041321355715236700170220ustar00rootroot00000000000000package critbitgo_test import ( "bytes" "math/rand" "sort" "testing" ) var keyCount int = 10000 var keyLen int = 128 var keys []string var alphabet string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" var alphalen int = len(alphabet) func init() { keys = make([]string, keyCount) random := rand.New(rand.NewSource(0)) for i := 0; i < len(keys); i++ { keys[i] = genRandomKey(random) } } func genRandomKey(rand *rand.Rand) string { buf := bytes.NewBufferString("") for i := 0; i < keyLen; i++ { buf.WriteByte(alphabet[rand.Intn(alphalen)]) } return buf.String() } func buildMap(keys []string) map[string]string { m := make(map[string]string) for _, key := range keys { m[key] = key } return m } func BenchmarkMapBuild(b *testing.B) { for i := 0; i < b.N; i++ { buildMap(keys) } } func BenchmarkSortedMapBuild(b *testing.B) { for i := 0; i < b.N; i++ { buildSortedMap(keys) } } func BenchmarkMapGet(b *testing.B) { m := buildMap(keys) random := rand.New(rand.NewSource(0)) b.ResetTimer() for i := 0; i < b.N; i++ { k := keys[random.Intn(keyCount)] _ = m[k] } } func BenchmarkSortedMapGet(b *testing.B) { m := buildSortedMap(keys) random := rand.New(rand.NewSource(0)) b.ResetTimer() for i := 0; i < b.N; i++ { k := keys[random.Intn(keyCount)] _, _ = m.Get(k) } } func BenchmarkMapDelete(b *testing.B) { m := buildMap(keys) random := rand.New(rand.NewSource(0)) b.ResetTimer() for i := 0; i < b.N; i++ { k := keys[random.Intn(keyCount)] if _, ok := m[k]; ok { delete(m, k) } } } func BenchmarkSortedMapDelete(b *testing.B) { m := buildSortedMap(keys) random := rand.New(rand.NewSource(0)) b.ResetTimer() for i := 0; i < b.N; i++ { k := keys[random.Intn(keyCount)] m.Delete(k) } } func BenchmarkMapKeys(b *testing.B) { m := buildMap(keys) b.ResetTimer() for i := 0; i < b.N; i++ { list := make([]string, 0, len(m)) for _, k := range m { list = append(list, k) } sort.Strings(list) } } func BenchmarkSortedMapKeys(b *testing.B) { m := buildSortedMap(keys) b.ResetTimer() for i := 0; i < b.N; i++ { m.Keys() } } critbitgo-1.4.0/map_test.go000066400000000000000000000074061355715236700156720ustar00rootroot00000000000000package critbitgo_test import ( "strings" "testing" "github.com/k-sone/critbitgo" ) func buildSortedMap(keys []string) *critbitgo.SortedMap { m := critbitgo.NewSortedMap() for _, key := range keys { m.Set(key, key) } return m } func TestSortedMapContains(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} m := buildSortedMap(keys) for _, key := range keys { if !m.Contains(key) { t.Errorf("Contains() - not found - [%s]", key) } } if m.Contains("aaa") { t.Error("Contains() - phantom found") } } func TestSortedMapGet(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} m := buildSortedMap(keys) for _, key := range keys { if value, ok := m.Get(key); !ok || value != key { t.Errorf("Get() - not found - [%s]", key) } } if value, ok := m.Get("aaa"); ok || value != nil { t.Error("Get() - phantom found") } } func TestSortedMapDelete(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} m := buildSortedMap(keys) for i, key := range keys { if !m.Contains(key) { t.Errorf("Delete() - not exists - [%s]", key) } if value, ok := m.Delete(key); !ok || value != key { t.Errorf("Delete() - failed - [%s]", key) } if m.Contains(key) { t.Errorf("Delete() - exists - [%s]", key) } if value, ok := m.Delete(key); ok || value != nil { t.Errorf("Delete() - phantom found - [%s]", key) } if i != len(keys) { for _, key2 := range keys[i+1:] { if !m.Contains(key2) { t.Errorf("Delete() - other not exists - [%s](%s)", key2, key) } } } } } func TestSortedMapSize(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} m := buildSortedMap(keys) klen := len(keys) if s := m.Size(); s != klen { t.Errorf("Size() - expected [%d], actual [%d]", klen, s) } for i, key := range keys { m.Delete(key) if s := m.Size(); s != klen-(i+1) { t.Errorf("Size() - expected [%d], actual [%d]", klen, s) } } } func TestSortedMapKeys(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} m := buildSortedMap(keys) skeys := m.Keys() for _, key := range keys { match := false for _, skey := range skeys { if key == skey { match = true break } } if !match { t.Errorf("Keys() - not found [%s]", key) } } } func TestSortedMapEach(t *testing.T) { keys := []string{"", "a", "aa", "b", "bb", "ab", "ba", "aba", "bab"} m := buildSortedMap(keys) elems := make(map[string]interface{}) handle := func(key string, value interface{}) bool { elems[key] = value return true } if !m.Each("", handle) { t.Error("Each() - invalid result") } for _, key := range keys { if _, ok := elems[key]; !ok { t.Errorf("Each() - not found [%s]", key) } else if value, ok := elems[key].(string); !ok || value != key { t.Errorf("Each() - invalid value [%s](%s)", value, key) } } elems = make(map[string]interface{}) handle = func(key string, value interface{}) bool { elems[key] = value return true } if !m.Each("b", handle) { t.Error("Each() - invalid result") } for _, key := range keys { if strings.Index(key, "b") == 0 { if _, ok := elems[key]; !ok { t.Errorf("Each() - not found [%s]", key) } else if value, ok := elems[key].(string); !ok || value != key { t.Errorf("Each() - invalid value [%s](%s)", value, key) } } else { if _, ok := elems[key]; ok { t.Errorf("Each() - phantom found [%s]", key) } } } elems = make(map[string]interface{}) handle = func(key string, value interface{}) bool { elems[key] = value return true } if !m.Each("c", handle) { t.Error("Each() - invalid result") } for _, key := range keys { if _, ok := elems[key]; ok { t.Errorf("Each() - phantom found [%s]", key) } } } critbitgo-1.4.0/net.go000066400000000000000000000175131355715236700146440ustar00rootroot00000000000000package critbitgo import ( "net" ) var ( mask32 = net.IPMask{0xff, 0xff, 0xff, 0xff} mask128 = net.IPMask{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ) // IP routing table. type Net struct { trie *Trie } // Add a route. // If `r` is not IPv4/IPv6 network, returns an error. func (n *Net) Add(r *net.IPNet, value interface{}) (err error) { var ip net.IP if ip, _, err = netValidateIPNet(r); err == nil { n.trie.Set(netIPNetToKey(ip, r.Mask), value) } return } // Add a route. // If `s` is not CIDR notation, returns an error. func (n *Net) AddCIDR(s string, value interface{}) (err error) { var r *net.IPNet if _, r, err = net.ParseCIDR(s); err == nil { n.Add(r, value) } return } // Delete a specific route. // If `r` is not IP4/IPv6 network or a route is not found, `ok` is false. func (n *Net) Delete(r *net.IPNet) (value interface{}, ok bool, err error) { var ip net.IP if ip, _, err = netValidateIPNet(r); err == nil { value, ok = n.trie.Delete(netIPNetToKey(ip, r.Mask)) } return } // Delete a specific route. // If `s` is not CIDR notation or a route is not found, `ok` is false. func (n *Net) DeleteCIDR(s string) (value interface{}, ok bool, err error) { var r *net.IPNet if _, r, err = net.ParseCIDR(s); err == nil { value, ok, err = n.Delete(r) } return } // Get a specific route. // If `r` is not IPv4/IPv6 network or a route is not found, `ok` is false. func (n *Net) Get(r *net.IPNet) (value interface{}, ok bool, err error) { var ip net.IP if ip, _, err = netValidateIPNet(r); err == nil { value, ok = n.trie.Get(netIPNetToKey(ip, r.Mask)) } return } // Get a specific route. // If `s` is not CIDR notation or a route is not found, `ok` is false. func (n *Net) GetCIDR(s string) (value interface{}, ok bool, err error) { var r *net.IPNet if _, r, err = net.ParseCIDR(s); err == nil { value, ok, err = n.Get(r) } return } // Return a specific route by using the longest prefix matching. // If `r` is not IPv4/IPv6 network or a route is not found, `route` is nil. func (n *Net) Match(r *net.IPNet) (route *net.IPNet, value interface{}, err error) { var ip net.IP if ip, _, err = netValidateIP(r.IP); err == nil { if k, v := n.match(netIPNetToKey(ip, r.Mask)); k != nil { route = netKeyToIPNet(k) value = v } } return } // Return a specific route by using the longest prefix matching. // If `s` is not CIDR notation, or a route is not found, `route` is nil. func (n *Net) MatchCIDR(s string) (route *net.IPNet, value interface{}, err error) { var r *net.IPNet if _, r, err = net.ParseCIDR(s); err == nil { route, value, err = n.Match(r) } return } // Return a bool indicating whether a route would be found func (n *Net) ContainedIP(ip net.IP) (contained bool, err error) { k, _, err := n.matchIP(ip) contained = k != nil return } // Return a specific route by using the longest prefix matching. // If `ip` is invalid IP, or a route is not found, `route` is nil. func (n *Net) MatchIP(ip net.IP) (route *net.IPNet, value interface{}, err error) { k, v, err := n.matchIP(ip) if k != nil { route = netKeyToIPNet(k) value = v } return } func (n *Net) matchIP(ip net.IP) (k []byte, v interface{}, err error) { var isV4 bool ip, isV4, err = netValidateIP(ip) if err != nil { return } var mask net.IPMask if isV4 { mask = mask32 } else { mask = mask128 } k, v = n.match(netIPNetToKey(ip, mask)) return } func (n *Net) match(key []byte) ([]byte, interface{}) { if n.trie.size > 0 { if node := lookup(&n.trie.root, key, false); node != nil { return node.external.key, node.external.value } } return nil, nil } func lookup(p *node, key []byte, backtracking bool) *node { if p.internal != nil { var direction int if p.internal.offset == len(key)-1 { // selecting the larger side when comparing the mask direction = 1 } else if backtracking { direction = 0 } else { direction = p.internal.direction(key) } if c := lookup(&p.internal.child[direction], key, backtracking); c != nil { return c } if direction == 1 { // search other node return lookup(&p.internal.child[0], key, true) } return nil } else { nlen := len(p.external.key) if nlen != len(key) { return nil } // check mask mask := p.external.key[nlen-1] if mask > key[nlen-1] { return nil } // compare both keys with mask div := int(mask >> 3) for i := 0; i < div; i++ { if p.external.key[i] != key[i] { return nil } } if mod := uint(mask & 0x07); mod > 0 { bit := 8 - mod if p.external.key[div] != key[div]&(0xff>>bit<> 3) if mod := uint(mask & 0x07); mod != 0 { bit = 8 - mod } } } wrapper := func(key []byte, value interface{}) bool { if bit != 0 { if prefix[div]>>bit != key[div]>>bit { return false } } return handle(netKeyToIPNet(key), value) } n.trie.Allprefixed(prefix[0:div], wrapper) } func walkMatch(p *node, key []byte, handle func(*net.IPNet, interface{}) bool) bool { if p.internal != nil { if !walkMatch(&p.internal.child[0], key, handle) { return false } if p.internal.offset >= len(key)-1 || key[p.internal.offset]&p.internal.bit > 0 { return walkMatch(&p.internal.child[1], key, handle) } return true } mask := p.external.key[len(p.external.key)-1] if key[len(key)-1] < mask { return true } div := int(mask >> 3) for i := 0; i < div; i++ { if p.external.key[i] != key[i] { return true } } if mod := uint(mask & 0x07); mod > 0 { bit := 8 - mod if p.external.key[div] != key[div]&(0xff>>bit< 0 { walkMatch(&n.trie.root, netIPNetToKey(r.IP, r.Mask), handle) } } // Deletes all routes. func (n *Net) Clear() { n.trie.Clear() } // Returns number of routes. func (n *Net) Size() int { return n.trie.Size() } // Create IP routing table func NewNet() *Net { return &Net{NewTrie()} } func netValidateIP(ip net.IP) (nIP net.IP, isV4 bool, err error) { if v4 := ip.To4(); v4 != nil { nIP = v4 isV4 = true } else if ip.To16() != nil { nIP = ip } else { err = &net.AddrError{Err: "Invalid IP address", Addr: ip.String()} } return } func netValidateIPNet(r *net.IPNet) (nIP net.IP, isV4 bool, err error) { if r == nil { err = &net.AddrError{Err: "IP network is nil"} return } return netValidateIP(r.IP) } func netIPNetToKey(ip net.IP, mask net.IPMask) []byte { // +--------------+------+ // | ip address.. | mask | // +--------------+------+ ones, _ := mask.Size() return append(ip, byte(ones)) } func netKeyToIPNet(k []byte) *net.IPNet { iplen := len(k) - 1 return &net.IPNet{ IP: net.IP(k[:iplen]), Mask: net.CIDRMask(int(k[iplen]), iplen*8), } } critbitgo-1.4.0/net_bench_test.go000066400000000000000000000025711355715236700170400ustar00rootroot00000000000000package critbitgo_test import ( "math/rand" "net" "testing" "github.com/k-sone/critbitgo" ) var routeCount int = 10000 var routes []string func init() { routes = make([]string, routeCount) random := rand.New(rand.NewSource(0)) for i := 0; i < len(routes); i++ { routes[i] = genRoute(random) } } func genRoute(rand *rand.Rand) string { ip := rand.Int31() mask := rand.Intn(33) ipnet := &net.IPNet{ IP: net.IP{byte(ip >> 24), byte(ip >> 16), byte(ip >> 8), byte(ip)}, Mask: net.CIDRMask(mask, 32), } return ipnet.String() } func buildNet(keys []string) *critbitgo.Net { tree := critbitgo.NewNet() for i := 0; i < len(keys); i++ { tree.AddCIDR(keys[i], nil) } return tree } func BenchmarkNetBuild(b *testing.B) { for i := 0; i < b.N; i++ { buildNet(routes) } } func BenchmarkNetGet(b *testing.B) { n := buildNet(routes) random := rand.New(rand.NewSource(0)) b.ResetTimer() for i := 0; i < b.N; i++ { k := routes[random.Intn(routeCount)] n.GetCIDR(k) } } func BenchmarkNetDelete(b *testing.B) { n := buildNet(routes) random := rand.New(rand.NewSource(0)) b.ResetTimer() for i := 0; i < b.N; i++ { k := routes[random.Intn(keyCount)] n.DeleteCIDR(k) } } func BenchmarkNetMatch(b *testing.B) { n := buildNet(routes) random := rand.New(rand.NewSource(0)) b.ResetTimer() for i := 0; i < b.N; i++ { s := genRoute(random) n.MatchCIDR(s) } } critbitgo-1.4.0/net_test.go000066400000000000000000000145621355715236700157040ustar00rootroot00000000000000package critbitgo_test import ( "net" "reflect" "testing" "github.com/k-sone/critbitgo" ) func TestNet(t *testing.T) { trie := critbitgo.NewNet() cidr := "192.168.1.0/24" host := "192.168.1.1/32" hostIP := net.IPv4(192, 168, 1, 1) if _, _, err := trie.GetCIDR(""); err == nil { t.Error("GetCIDR() - not error") } if v, ok, err := trie.GetCIDR(cidr); v != nil || ok || err != nil { t.Errorf("GetCIDR() - phantom: %v, %v, %v", v, ok, err) } if _, _, err := trie.MatchCIDR(""); err == nil { t.Error("MatchCIDR() - not error") } if r, v, err := trie.MatchCIDR(host); r != nil || v != nil || err != nil { t.Errorf("MatchCIDR() - phantom: %v, %v, %v", r, v, err) } if _, _, err := trie.MatchIP(net.IP([]byte{})); err == nil { t.Error("MatchIP() - not error") } if r, v, err := trie.MatchIP(hostIP); r != nil || v != nil || err != nil { t.Errorf("MatchIP() - phantom: %v, %v, %v", r, v, err) } if _, err := trie.ContainedIP(net.IP([]byte{})); err == nil { t.Error("ContainedIP() - not error") } if b, err := trie.ContainedIP(hostIP); b || err != nil { t.Errorf("ContainedIP() - phantom: %v, %v", b, err) } if _, _, err := trie.DeleteCIDR(""); err == nil { t.Error("DeleteCIDR() - not error") } if v, ok, err := trie.DeleteCIDR(cidr); v != nil || ok || err != nil { t.Errorf("DeleteCIDR() - phantom: %v, %v, %v", v, ok, err) } if err := trie.AddCIDR(cidr, &cidr); err != nil { t.Errorf("AddCIDR() - %s: error occurred %s", cidr, err) } if v, ok, err := trie.GetCIDR(cidr); v != &cidr || !ok || err != nil { t.Errorf("GetCIDR() - failed: %v, %v, %v", v, ok, err) } if r, v, err := trie.MatchCIDR(host); r == nil || r.String() != cidr || v != &cidr || err != nil { t.Errorf("MatchCIDR() - failed: %v, %v, %v", r, v, err) } if r, v, err := trie.MatchIP(hostIP); r == nil || r.String() != cidr || v != &cidr || err != nil { t.Errorf("MatchIP() - failed: %v, %v, %v", r, v, err) } if b, err := trie.ContainedIP(hostIP); !b || err != nil { t.Errorf("ContainedIP() - failed: %v, %v", b, err) } if v, ok, err := trie.DeleteCIDR(cidr); v != &cidr || !ok || err != nil { t.Errorf("DeleteCIDR() - failed: %v, %v, %v", v, ok, err) } } func checkMatch(t *testing.T, trie *critbitgo.Net, request, expect string) { route, value, err := trie.MatchCIDR(request) if err != nil { t.Errorf("MatchCIDR() - %s: error occurred %s", request, err) } if cidr := route.String(); expect != cidr { t.Errorf("MatchCIDR() - %s: expected [%s], actual [%s]", request, expect, cidr) } if value == nil { t.Errorf("MatchCIDR() - %s: no value", request) } } func buildTestNet(t *testing.T) *critbitgo.Net { trie := critbitgo.NewNet() cidrs := []string{ "10.0.0.0/8", "192.168.0.0/16", "192.168.1.0/24", "192.168.1.0/28", "192.168.1.0/32", "192.168.1.1/32", "192.168.1.2/32", "192.168.1.32/27", "192.168.1.32/30", "192.168.2.1/32", "192.168.2.2/32", } for i, cidr := range cidrs { if err := trie.AddCIDR(cidr, cidrs[i]); err != nil { t.Errorf("AddCIDR() - %s: error occurred %s", cidr, err) } } return trie } func TestNetMatch(t *testing.T) { trie := buildTestNet(t) checkMatch(t, trie, "10.0.0.0/24", "10.0.0.0/8") checkMatch(t, trie, "192.168.1.0/24", "192.168.1.0/24") checkMatch(t, trie, "192.168.1.0/30", "192.168.1.0/28") checkMatch(t, trie, "192.168.1.0/32", "192.168.1.0/32") checkMatch(t, trie, "192.168.1.128/26", "192.168.1.0/24") checkMatch(t, trie, "192.168.2.128/26", "192.168.0.0/16") checkMatch(t, trie, "192.168.1.1/32", "192.168.1.1/32") checkMatch(t, trie, "192.168.1.2/32", "192.168.1.2/32") checkMatch(t, trie, "192.168.1.3/32", "192.168.1.0/28") checkMatch(t, trie, "192.168.1.32/32", "192.168.1.32/30") checkMatch(t, trie, "192.168.1.35/32", "192.168.1.32/30") checkMatch(t, trie, "192.168.1.36/32", "192.168.1.32/27") checkMatch(t, trie, "192.168.1.63/32", "192.168.1.32/27") checkMatch(t, trie, "192.168.1.64/32", "192.168.1.0/24") checkMatch(t, trie, "192.168.2.2/32", "192.168.2.2/32") checkMatch(t, trie, "192.168.2.3/32", "192.168.0.0/16") } func TestNetWalk(t *testing.T) { trie := buildTestNet(t) var c int f := func(n *net.IPNet, v interface{}) bool { c += 1 return true } c = 0 trie.Walk(nil, f) if c != 11 { t.Errorf("Walk() - %d: full walk", c) } _, s, _ := net.ParseCIDR("192.168.1.1/32") c = 0 trie.Walk(s, f) if c != 6 { t.Errorf("Walk() - %d: has start route", c) } _, s, _ = net.ParseCIDR("10.0.0.0/0") c = 0 trie.Walk(s, f) if c != 0 { t.Errorf("Walk() - %d: not found start route", c) } } func TestNetWalkPrefix(t *testing.T) { trie := buildTestNet(t) var ret, exp []string f := func(n *net.IPNet, _ interface{}) bool { ret = append(ret, n.String()) return true } ret = []string{} exp = []string{"192.168.0.0/16"} _, s, _ := net.ParseCIDR("192.168.0.0/24") trie.WalkPrefix(s, f) if !reflect.DeepEqual(ret, exp) { t.Errorf("WalkPrefix() - failed %s", ret) } ret = []string{} exp = []string{ "192.168.0.0/16", "192.168.1.0/24", "192.168.1.0/28", "192.168.1.0/32", "192.168.1.1/32", "192.168.1.2/32", "192.168.1.32/27", "192.168.1.32/30", } _, s, _ = net.ParseCIDR("192.168.0.0/23") trie.WalkPrefix(s, f) if !reflect.DeepEqual(ret, exp) { t.Errorf("WalkPrefix() - failed %s", ret) } ret = []string{} exp = []string{} _, s, _ = net.ParseCIDR("0.0.0.0/16") trie.WalkPrefix(s, f) if !reflect.DeepEqual(ret, exp) { t.Errorf("WalkPrefix() - failed %s", ret) } } func TestNetWalkMatch(t *testing.T) { trie := buildTestNet(t) var ret, exp []string f := func(n *net.IPNet, _ interface{}) bool { ret = append(ret, n.String()) return true } ret = []string{} exp = []string{"192.168.0.0/16", "192.168.1.0/24"} _, s, _ := net.ParseCIDR("192.168.1.0/27") trie.WalkMatch(s, f) if !reflect.DeepEqual(ret, exp) { t.Errorf("WalkMatch() - failed %s", ret) } ret = []string{} exp = []string{ "192.168.0.0/16", "192.168.1.0/24", "192.168.1.0/28", "192.168.1.1/32", } _, s, _ = net.ParseCIDR("192.168.1.1/32") trie.WalkMatch(s, f) if !reflect.DeepEqual(ret, exp) { t.Errorf("WalkMatch() - failed %s", ret) } ret = []string{} exp = []string{"10.0.0.0/8"} _, s, _ = net.ParseCIDR("10.0.64.0/18") trie.WalkMatch(s, f) if !reflect.DeepEqual(ret, exp) { t.Errorf("WalkMatch() - failed %s", ret) } ret = []string{} exp = []string{} _, s, _ = net.ParseCIDR("255.255.255.0/24") trie.WalkMatch(s, f) if !reflect.DeepEqual(ret, exp) { t.Errorf("WalkMatch() - failed %s", ret) } }