123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- package iradix
- import (
- "bytes"
- "github.com/hashicorp/golang-lru/simplelru"
- )
- const (
- // defaultModifiedCache is the default size of the modified node
- // cache used per transaction. This is used to cache the updates
- // to the nodes near the root, while the leaves do not need to be
- // cached. This is important for very large transactions to prevent
- // the modified cache from growing to be enormous.
- defaultModifiedCache = 8192
- )
- // Tree implements an immutable 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 immutability
- // means that it is safe to concurrently read from a Tree without any
- // coordination.
- 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
- }
- // Txn is a transaction on the tree. This transaction is applied
- // atomically and returns a new tree when committed. A transaction
- // is not thread safe, and should only be used by a single goroutine.
- type Txn struct {
- root *Node
- size int
- modified *simplelru.LRU
- }
- // Txn starts a new transaction that can be used to mutate the tree
- func (t *Tree) Txn() *Txn {
- txn := &Txn{
- root: t.root,
- size: t.size,
- }
- return txn
- }
- // writeNode returns a node to be modified, if the current
- // node as already been modified during the course of
- // the transaction, it is used in-place.
- func (t *Txn) writeNode(n *Node) *Node {
- // Ensure the modified set exists
- if t.modified == nil {
- lru, err := simplelru.NewLRU(defaultModifiedCache, nil)
- if err != nil {
- panic(err)
- }
- t.modified = lru
- }
- // If this node has already been modified, we can
- // continue to use it during this transaction.
- if _, ok := t.modified.Get(n); ok {
- return n
- }
- // Copy the existing node
- nc := new(Node)
- if n.prefix != nil {
- nc.prefix = make([]byte, len(n.prefix))
- copy(nc.prefix, n.prefix)
- }
- if n.leaf != nil {
- nc.leaf = new(leafNode)
- *nc.leaf = *n.leaf
- }
- if len(n.edges) != 0 {
- nc.edges = make([]edge, len(n.edges))
- copy(nc.edges, n.edges)
- }
- // Mark this node as modified
- t.modified.Add(n, nil)
- return nc
- }
- // insert does a recursive insertion
- func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface{}, bool) {
- // Handle key exhaution
- if len(search) == 0 {
- nc := t.writeNode(n)
- if n.isLeaf() {
- old := nc.leaf.val
- nc.leaf.val = v
- return nc, old, true
- } else {
- nc.leaf = &leafNode{
- key: k,
- val: v,
- }
- return nc, nil, false
- }
- }
- // Look for the edge
- idx, child := n.getEdge(search[0])
- // No edge, create one
- if child == nil {
- e := edge{
- label: search[0],
- node: &Node{
- leaf: &leafNode{
- key: k,
- val: v,
- },
- prefix: search,
- },
- }
- nc := t.writeNode(n)
- nc.addEdge(e)
- return nc, nil, false
- }
- // Determine longest prefix of the search key on match
- commonPrefix := longestPrefix(search, child.prefix)
- if commonPrefix == len(child.prefix) {
- search = search[commonPrefix:]
- newChild, oldVal, didUpdate := t.insert(child, k, search, v)
- if newChild != nil {
- nc := t.writeNode(n)
- nc.edges[idx].node = newChild
- return nc, oldVal, didUpdate
- }
- return nil, oldVal, didUpdate
- }
- // Split the node
- nc := t.writeNode(n)
- splitNode := &Node{
- prefix: search[:commonPrefix],
- }
- nc.replaceEdge(edge{
- label: search[0],
- node: splitNode,
- })
- // Restore the existing child node
- modChild := t.writeNode(child)
- splitNode.addEdge(edge{
- label: modChild.prefix[commonPrefix],
- node: modChild,
- })
- modChild.prefix = modChild.prefix[commonPrefix:]
- // Create a new leaf node
- leaf := &leafNode{
- key: k,
- val: v,
- }
- // If the new key is a subset, add to to this node
- search = search[commonPrefix:]
- if len(search) == 0 {
- splitNode.leaf = leaf
- return nc, nil, false
- }
- // Create a new edge for the node
- splitNode.addEdge(edge{
- label: search[0],
- node: &Node{
- leaf: leaf,
- prefix: search,
- },
- })
- return nc, nil, false
- }
- // delete does a recursive deletion
- func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) {
- // Check for key exhaution
- if len(search) == 0 {
- if !n.isLeaf() {
- return nil, nil
- }
- // Remove the leaf node
- nc := t.writeNode(n)
- nc.leaf = nil
- // Check if this node should be merged
- if n != t.root && len(nc.edges) == 1 {
- nc.mergeChild()
- }
- return nc, n.leaf
- }
- // Look for an edge
- label := search[0]
- idx, child := n.getEdge(label)
- if child == nil || !bytes.HasPrefix(search, child.prefix) {
- return nil, nil
- }
- // Consume the search prefix
- search = search[len(child.prefix):]
- newChild, leaf := t.delete(n, child, search)
- if newChild == nil {
- return nil, nil
- }
- // Copy this node
- nc := t.writeNode(n)
- // Delete the edge if the node has no edges
- if newChild.leaf == nil && len(newChild.edges) == 0 {
- nc.delEdge(label)
- if n != t.root && len(nc.edges) == 1 && !nc.isLeaf() {
- nc.mergeChild()
- }
- } else {
- nc.edges[idx].node = newChild
- }
- return nc, leaf
- }
- // Insert is used to add or update a given key. The return provides
- // the previous value and a bool indicating if any was set.
- func (t *Txn) Insert(k []byte, v interface{}) (interface{}, bool) {
- newRoot, oldVal, didUpdate := t.insert(t.root, k, k, v)
- if newRoot != nil {
- t.root = newRoot
- }
- if !didUpdate {
- t.size++
- }
- return oldVal, didUpdate
- }
- // Delete is used to delete a given key. Returns the old value if any,
- // and a bool indicating if the key was set.
- func (t *Txn) Delete(k []byte) (interface{}, bool) {
- newRoot, leaf := t.delete(nil, t.root, k)
- if newRoot != nil {
- t.root = newRoot
- }
- if leaf != nil {
- t.size--
- return leaf.val, true
- }
- return nil, false
- }
- // Root returns the current root of the radix tree within this
- // transaction. The root is not safe across insert and delete operations,
- // but can be used to read the current state during a transaction.
- func (t *Txn) Root() *Node {
- return t.root
- }
- // Get is used to lookup a specific key, returning
- // the value and if it was found
- func (t *Txn) Get(k []byte) (interface{}, bool) {
- return t.root.Get(k)
- }
- // Commit is used to finalize the transaction and return a new tree
- func (t *Txn) Commit() *Tree {
- t.modified = nil
- return &Tree{t.root, t.size}
- }
- // Insert is used to add or update a given key. The return provides
- // the new tree, previous value and a bool indicating if any was set.
- func (t *Tree) Insert(k []byte, v interface{}) (*Tree, interface{}, bool) {
- txn := t.Txn()
- old, ok := txn.Insert(k, v)
- return txn.Commit(), old, ok
- }
- // Delete is used to delete a given key. Returns the new tree,
- // old value if any, and a bool indicating if the key was set.
- func (t *Tree) Delete(k []byte) (*Tree, interface{}, bool) {
- txn := t.Txn()
- old, ok := txn.Delete(k)
- return txn.Commit(), old, ok
- }
- // Root returns the root node of the tree which can be used for richer
- // query operations.
- func (t *Tree) Root() *Node {
- return t.root
- }
- // Get is used to lookup a specific key, returning
- // the value and if it was found
- func (t *Tree) Get(k []byte) (interface{}, bool) {
- return t.root.Get(k)
- }
- // longestPrefix finds the length of the shared prefix
- // of two strings
- func longestPrefix(k1, k2 []byte) 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
- }
- // concat two byte slices, returning a third new copy
- func concat(a, b []byte) []byte {
- c := make([]byte, len(a)+len(b))
- copy(c, a)
- copy(c[len(a):], b)
- return c
- }
|