influxdb/pkg/radix/tree.go

429 lines
8.1 KiB
Go

package radix
// This is a fork of https://github.com/armon/go-radix that removes the
// ability to update nodes as well as uses fixed int value type.
import (
"bytes"
"sort"
"sync"
)
// leafNode is used to represent a value
type leafNode struct {
valid bool // true if key/val are valid
key []byte
val int
}
// 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 []byte
// 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.valid
}
func (n *node) addEdge(e edge) {
// find the insertion point with bisection
num := len(n.edges)
i, j := 0, num
for i < j {
h := int(uint(i+j) >> 1)
if n.edges[h].label < e.label {
i = h + 1
} else {
j = h
}
}
// make room, copy the suffix, and insert.
n.edges = append(n.edges, edge{})
copy(n.edges[i+1:], n.edges[i:])
n.edges[i] = e
}
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 {
// linear search for small slices
if len(n.edges) < 16 {
for _, e := range n.edges {
if e.label == label {
return e.node
}
}
return nil
}
// binary search for larger
num := len(n.edges)
i, j := 0, num
for i < j {
h := int(uint(i+j) >> 1)
if n.edges[h].label < label {
i = h + 1
} else {
j = h
}
}
if i < num && n.edges[i].label == label {
return n.edges[i].node
}
return nil
}
type edges []edge
// 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. The tree is safe for concurrent access.
type Tree struct {
mu sync.RWMutex
root *node
size int
buf buffer
}
// New returns an empty Tree
func New() *Tree {
return &Tree{root: &node{}}
}
// NewFromMap returns a new tree containing the keys
// from an existing map
func NewFromMap(m map[string]int) *Tree {
t := &Tree{root: &node{}}
for k, v := range m {
t.Insert([]byte(k), v)
}
return t
}
// Len is used to return the number of elements in the tree
func (t *Tree) Len() int {
t.mu.RLock()
size := t.size
t.mu.RUnlock()
return size
}
// longestPrefix finds the length of the shared prefix
// of two strings
func longestPrefix(k1, k2 []byte) int {
// for loops can't be inlined, but goto's can. we also use uint to help
// out the compiler to prove bounds checks aren't necessary on the index
// operations.
lk1, lk2 := uint(len(k1)), uint(len(k2))
i := uint(0)
loop:
if lk1 <= i || lk2 <= i {
return int(i)
}
if k1[i] != k2[i] {
return int(i)
}
i++
goto loop
}
// Insert is used to add a newentry or update
// an existing entry. Returns if inserted.
func (t *Tree) Insert(s []byte, v int) (int, bool) {
t.mu.RLock()
var parent *node
n := t.root
search := s
for {
// Handle key exhaution
if len(search) == 0 {
if n.isLeaf() {
old := n.leaf.val
t.mu.RUnlock()
return old, false
}
n.leaf = leafNode{
key: t.buf.Copy(s),
val: v,
valid: true,
}
t.size++
t.mu.RUnlock()
return v, true
}
// Look for the edge
parent = n
n = n.getEdge(search[0])
// No edge, create one
if n == nil {
newNode := &node{
leaf: leafNode{
key: t.buf.Copy(s),
val: v,
valid: true,
},
prefix: t.buf.Copy(search),
}
e := edge{
label: search[0],
node: newNode,
}
parent.addEdge(e)
t.size++
t.mu.RUnlock()
return v, true
}
// 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: t.buf.Copy(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:]
// Create a new leaf node
leaf := leafNode{
key: t.buf.Copy(s),
val: v,
valid: true,
}
// If the new key is a subset, add to to this node
search = search[commonPrefix:]
if len(search) == 0 {
child.leaf = leaf
t.mu.RUnlock()
return v, true
}
// Create a new edge for the node
child.addEdge(edge{
label: search[0],
node: &node{
leaf: leaf,
prefix: t.buf.Copy(search),
},
})
t.mu.RUnlock()
return v, 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 []byte) int {
t.mu.Lock()
defer t.mu.Unlock()
return t.deletePrefix(nil, t.root, s)
}
// delete does a recursive deletion
func (t *Tree) deletePrefix(parent, n *node, prefix []byte) 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 []byte, v int) bool {
subTreeSize++
return false
})
if n.isLeaf() {
n.leaf = leafNode{}
}
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 || (!bytes.HasPrefix(child.prefix, prefix) && !bytes.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
prefix := make([]byte, 0, len(n.prefix)+len(child.prefix))
prefix = append(prefix, n.prefix...)
prefix = append(prefix, child.prefix...)
n.prefix = 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 []byte) (int, bool) {
t.mu.RLock()
n := t.root
search := s
for {
// Check for key exhaution
if len(search) == 0 {
if n.isLeaf() {
t.mu.RUnlock()
return n.leaf.val, true
}
break
}
// Look for an edge
n = n.getEdge(search[0])
if n == nil {
break
}
// Consume the search prefix
if bytes.HasPrefix(search, n.prefix) {
search = search[len(n.prefix):]
} else {
break
}
}
t.mu.RUnlock()
return 0, false
}
// walkFn is used when walking the tree. Takes a
// key and value, returning if iteration should
// be terminated.
type walkFn func(s []byte, v int) bool
// 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.valid && 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
}
// Minimum is used to return the minimum value in the tree
func (t *Tree) Minimum() ([]byte, int, bool) {
t.mu.RLock()
n := t.root
for {
if n.isLeaf() {
t.mu.RUnlock()
return n.leaf.key, n.leaf.val, true
}
if len(n.edges) > 0 {
n = n.edges[0].node
} else {
break
}
}
t.mu.RUnlock()
return nil, 0, false
}
// Maximum is used to return the maximum value in the tree
func (t *Tree) Maximum() ([]byte, int, bool) {
t.mu.RLock()
n := t.root
for {
if num := len(n.edges); num > 0 {
n = n.edges[num-1].node
continue
}
if n.isLeaf() {
t.mu.RUnlock()
return n.leaf.key, n.leaf.val, true
}
break
}
t.mu.RUnlock()
return nil, 0, false
}