Skip to content

Commit

Permalink
add radix package start
Browse files Browse the repository at this point in the history
  • Loading branch information
SaltedCashew committed Sep 19, 2016
1 parent 7f4a18d commit 01695f5
Showing 1 changed file with 330 additions and 0 deletions.
330 changes: 330 additions & 0 deletions bs_radix/bs_radix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
package bs_radix

import (
"sort"
"strings"
"fmt"
)


// 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) replaceEdge(e edge) {
num := len(n.edges)
idx := sort.Search(num, func(i int) bool {
return n.edges[i].label >= e.label
})
if idx < num && n.edges[idx].label == e.label {
n.edges[idx].node = e.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 {
t := &Tree{root: &node{}}
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,

},
}
fmt.Println("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],

}
fmt.Println("Prefix: ", search[:commonPrefix])
parent.replaceEdge(edge{
label: search[0],
node: child,
})

// Restore the existing node
child.addEdge(edge{
label: n.prefix[commonPrefix],
node: n,
})
n.prefix = n.prefix[commonPrefix:]
fmt.Println("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,

},

})
fmt.Println("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
}

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
}



0 comments on commit 01695f5

Please sign in to comment.