// Package gomapllrb implements an in-memory key/value store using LLRB algorithm. // LLRB (Left-Leaning Red-Black) is a self-balancing binary search tree that // stores the keys in order which allows ordered iteration and find nearest keys. // // Copyright (c) 2023, Seungyoung Kim // https://github.com/wolkykim/GoMapLLRB package gomapllrb import ( "bytes" "fmt" "sync" "golang.org/x/exp/constraints" ) const ( // LLRB234 sets the tree management property and algorithm. LLRB234 = true // true: 2-3-4 varian(default), false: 2-3 variant ) // Tree is the glorious tree struct. type Tree[K constraints.Ordered] struct { isLess Comparator[K] // data comparator (default: string comparator) root *Node[K] // root node len int // number of object stored mutex sync.RWMutex // reader/writer mutual exclusion lock stats Stats // usage and performance metrics } // Node is like an apple on the apple trees. type Node[K constraints.Ordered] struct { name K data interface{} red bool up *Node[K] left *Node[K] right *Node[K] } // Stats provides usage statistics accessible via Stats() method. type Stats struct { Put struct { Sum uint64 New uint64 Update uint64 } Delete struct { Sum uint64 Deleted uint64 NotFound uint64 } Get struct { Sum uint64 Found uint64 NotFound uint64 } Perf PerfStats } // PerfStats are global stats for debugging purpose. type PerfStats struct { Flip uint64 Rotate struct { Sum uint64 Left uint64 Right uint64 } } // New creates a new tree. func New[K constraints.Ordered]() *Tree[K] { return &Tree[K]{ isLess: IsLess[K], } } // SetLess sets a user comparator function. // // func myLess[K constraints.Ordered](a, b K) bool { // // return true if a < b, or false // } func (tree *Tree[K]) SetLess(fn Comparator[K]) { tree.isLess = fn } // Put inserts a new key or replaces old if the same key is found. func (tree *Tree[K]) Put(name K, data interface{}) { tree.mutex.Lock() defer tree.mutex.Unlock() tree.root = tree.put(tree.root, name, data) tree.root.red = false } // Delete deletes the key. It returns an error if the key is not found. func (tree *Tree[K]) Delete(name K) bool { tree.mutex.Lock() defer tree.mutex.Unlock() var deleted bool tree.root, deleted = tree.delete(tree.root, name) if tree.root != nil { tree.root.red = false } return deleted } // Get returns the value of the key. If key is not found, it returns Nil. // When Nil value is expected as a actual value, use Exist() instead. func (tree *Tree[K]) Get(name K) interface{} { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := tree.get(tree.root, name); node != nil { return node.data } return nil } // Exist checks if the key exists. func (tree *Tree[K]) Exist(name K) bool { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := tree.get(tree.root, name); node != nil { return true } return false } // Min returns a min key and value. func (tree *Tree[K]) Min() (K, interface{}, bool) { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := findMin(tree.root); node != nil { return node.name, node.data, true } var n K return n, nil, false } // Max returns a max key and value. func (tree *Tree[K]) Max() (K, interface{}, bool) { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := findMax(tree.root); node != nil { return node.name, node.data, true } var n K return n, nil, false } // Bigger finds the next key bigger than given ken. func (tree *Tree[K]) Bigger(name K) (K, interface{}, bool) { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := tree.bigger(tree.root, name, false); node != nil { return node.name, node.data, true } var n K return n, nil, false } // Smaller finds the next key bigger than given ken. func (tree *Tree[K]) Smaller(name K) (K, interface{}, bool) { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := tree.smaller(tree.root, name, false); node != nil { return node.name, node.data, true } var n K return n, nil, false } // EqualOrBigger finds a matching key or the next bigger key. func (tree *Tree[K]) EqualOrBigger(name K) (K, interface{}, bool) { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := tree.bigger(tree.root, name, true); node != nil { return node.name, node.data, true } var n K return n, nil, false } // EqualOrSmaller finds a matching key or the next smaller key. func (tree *Tree[K]) EqualOrSmaller(name K) (K, interface{}, bool) { tree.mutex.RLock() defer tree.mutex.RUnlock() if node := tree.smaller(tree.root, name, true); node != nil { return node.name, node.data, true } var n K return n, nil, false } // Clear empties the tree without resetting the statistic metrics. func (tree *Tree[K]) Clear() { tree.mutex.Lock() defer tree.mutex.Unlock() tree.root = nil tree.len = 0 } // Len returns the number of object stored. func (tree *Tree[K]) Len() int { return tree.len } // Stats returns a copy of the statistics metrics. func (tree *Tree[K]) Stats() Stats { tree.stats.Put.Sum = tree.stats.Put.New + tree.stats.Put.Update tree.stats.Get.Sum = tree.stats.Get.Found + tree.stats.Get.NotFound tree.stats.Delete.Sum = tree.stats.Delete.Deleted + tree.stats.Delete.NotFound tree.stats.Perf = pstats tree.stats.Perf.Rotate.Sum = tree.stats.Perf.Rotate.Left + tree.stats.Perf.Rotate.Right return tree.stats } // ResetStats resets all the satistics metrics. func (tree *Tree[K]) ResetStats() { tree.stats = Stats{} pstats = PerfStats{} } // String returns a pretty drawing of the tree structure. // // ┌── 6 // | └──[5] // 4 // │ ┌── 3 // └──[2] // └── 1 func (tree *Tree[K]) String() string { var buf bytes.Buffer tree.mutex.RLock() defer tree.mutex.RUnlock() printNode(tree.root, &buf, nil, false) return buf.String() } // Map returns the tree in a map func (tree *Tree[K]) Map() map[K]interface{} { m := make(map[K]interface{}, tree.Len()) for it := tree.Iter(); it.Next(); { m[it.Key()] = it.Val() } return m } // String returns a statistics data in a string. func (s Stats) String() string { variant := "234" if !LLRB234 { variant = "23" } numUpdate := s.Put.Sum + s.Delete.Sum return fmt.Sprintf("Variant:LLRB%s, Put:%d, Delete:%d, Get:%d, Rotate:%0.2f, Flip:%0.2f", variant, s.Put.Sum, s.Delete.Sum, s.Get.Sum, float64(s.Perf.Rotate.Sum)/float64(numUpdate), float64(s.Perf.Flip)/float64(numUpdate)) } // Check checks that the invariants of the red-black tree are satisfied. // // Root property: The root is black. // Red property: If a node is red, then both its children are black. // Black property: For each node, all simple paths from the node to // descendant leaves contain the same number of black nodes. // LLRB property: 3-nodes always lean to the left and 4-nodes are balanced. func (tree *Tree[K]) Check() error { if err := checkRoot(tree.root); err != nil { return err } if err := checkRed(tree.root); err != nil { return err } length := 0 if err := checkBlack(tree.root, &length); err != nil { return err } return checkLLRB(tree.root) } /************************************************************************* * Iterator ************************************************************************/ // Iter is a iterator object. type Iter[K constraints.Ordered] struct { tree *Tree[K] cur *Node[K] // cursor, start from last *Node[K] // last node pointer after next() end K // end boundary is span is set span bool // indicates the end boundary is set done bool // indicates the iteration is complete } // Iter returns an iterator. func (tree *Tree[K]) Iter() *Iter[K] { tree.mutex.RLock() defer tree.mutex.RUnlock() it := &Iter[K]{ tree: tree, cur: findMin(tree.root), } if it.cur == nil { it.done = true } return it } // Range returns a ranged iterator. func (tree *Tree[K]) Range(start, end K) *Iter[K] { tree.mutex.RLock() defer tree.mutex.RUnlock() it := &Iter[K]{ tree: tree, cur: tree.bigger(tree.root, start, true), end: end, span: true, } if it.cur == nil { it.done = true } return it } // Next travels the keys in the tree. func (it *Iter[K]) Next() bool { if it.done { return false } it.last = it.cur it.tree.mutex.RLock() defer it.tree.mutex.RUnlock() if it.cur = it.tree.bigger(it.cur.right, it.cur.name, false); it.cur == nil { it.cur = it.last.up // go up until bigger value found for it.cur != nil && it.tree.isLess(it.cur.name, it.last.name) { it.cur = it.cur.up } // go down again if it.cur != nil { it.cur = it.tree.bigger(it.cur, it.last.name, false) } if it.cur == nil { it.done = true } } if !it.done && it.span && it.tree.isLess(it.end, it.cur.name) { it.done = true } return true } // Key returns the key name. func (it *Iter[K]) Key() K { if it.last == nil { var k K return k } return it.last.name } // Val returns the value data. func (it *Iter[K]) Val() interface{} { if it.last == nil { return nil } return it.last.data } /************************************************************************* * Default comparators ************************************************************************/ // Comparator is the type. type Comparator[K constraints.Ordered] func(a, b K) bool // IsLess is the default comparator. func IsLess[K constraints.Ordered](a, b K) bool { return a < b } /************************************************************************* * User data manipulation functions ************************************************************************/ func (tree *Tree[K]) put(node *Node[K], name K, data interface{}) *Node[K] { if node == nil { tree.len++ tree.stats.Put.New++ return newNode[K](name, data) } if LLRB234 { // split 4-nodes on the way down if isRed(node.left) && isRed(node.right) { flipColor(node) } } if tree.isLess(name, node.name) { node.left = tree.put(node.left, name, data) node.left.up = node } else if tree.isLess(node.name, name) { node.right = tree.put(node.right, name, data) node.right.up = node } else { // existing key found node.data = data tree.stats.Put.Update++ } // fix right-leaning reds on the way up if isRed(node.right) && !isRed(node.left) { node = rotateLeft(node) } // fix two reds in a row on the way up if isRed(node.left) && isRed(node.left.left) { node = rotateRight(node) } if !LLRB234 { // split 4-nodes on the way up if isRed(node.left) && isRed(node.right) { flipColor(node) } } // return new root return node } func (tree *Tree[K]) delete(node *Node[K], name K) (*Node[K], bool) { if node == nil { tree.stats.Delete.NotFound++ return nil, false } deleted := false if tree.isLess(name, node.name) { // move red left if node.left != nil && (!isRed(node.left) && !isRed(node.left.left)) { node = moveRedLeft(node) } // keep going down to the left node.left, deleted = tree.delete(node.left, name) } else { // right or equal if isRed(node.left) { node = rotateRight(node) } // remove if equal at the bottom if node.right == nil && !tree.isLess(node.name, name) { tree.len-- tree.stats.Delete.Deleted++ return nil, true } // move red right if node.right != nil && (!isRed(node.right) && !isRed(node.right.left)) { node = moveRedRight(node) } // found in the middle if !tree.isLess(node.name, name) { // we delete the min node from the right instead var min *Node[K] node.right, min = deleteMin(node.right) // then copy the min node to this node.name = min.name node.data = min.data tree.len-- tree.stats.Delete.Deleted++ } else { // keep going down to the right node.right, deleted = tree.delete(node.right, name) } } // fix right-leaning red nodes on the way up return fixNode(node), deleted } func (tree *Tree[K]) get(node *Node[K], name K) *Node[K] { // do linear search for performance for node != nil { if tree.isLess(name, node.name) { node = node.left } else if tree.isLess(node.name, name) { node = node.right } else { tree.stats.Get.Found++ return node } } tree.stats.Get.NotFound++ return nil } func (tree *Tree[K]) bigger(node *Node[K], name K, equal bool) *Node[K] { if node == nil { return nil } this := node if tree.isLess(name, node.name) { if node = tree.bigger(node.left, name, equal); node == nil { node = this } } else if tree.isLess(node.name, name) { node = tree.bigger(node.right, name, equal) } else if !equal { // match found, continue to the right node = tree.bigger(node.right, name, equal) } return node } func (tree *Tree[K]) smaller(node *Node[K], name K, equal bool) *Node[K] { if node == nil { return nil } this := node if tree.isLess(name, node.name) { node = tree.smaller(node.left, name, equal) } else if tree.isLess(node.name, name) { if node = tree.smaller(node.right, name, equal); node == nil { node = this } } else if !equal { // match found, continue to the left node = tree.smaller(node.left, name, equal) } return node } /************************************************************************* * Tree property management functions ************************************************************************/ var pstats PerfStats func newNode[K constraints.Ordered](name K, data interface{}) *Node[K] { return &Node[K]{ name: name, data: data, red: true, } } func isRed[K constraints.Ordered](node *Node[K]) bool { if node == nil { return false } return node.red } func flipColor[K constraints.Ordered](node *Node[K]) { node.red = !node.red node.left.red = !node.left.red node.right.red = !node.right.red pstats.Flip++ } func rotateLeft[K constraints.Ordered](node *Node[K]) *Node[K] { n := node.right n.up = node.up node.up = n node.right = n.left n.left = node n.red = n.left.red n.left.red = true pstats.Rotate.Left++ return n } func rotateRight[K constraints.Ordered](node *Node[K]) *Node[K] { n := node.left n.up = node.up node.up = n node.left = n.right n.right = node n.red = n.right.red n.right.red = true pstats.Rotate.Right++ return n } func moveRedLeft[K constraints.Ordered](node *Node[K]) *Node[K] { flipColor(node) if isRed(node.right.left) { node.right = rotateRight(node.right) node = rotateLeft(node) flipColor(node) if LLRB234 { // 2-3-4 exclusive if isRed(node.right.right) { node.right = rotateLeft(node.right) } } } return node } func moveRedRight[K constraints.Ordered](node *Node[K]) *Node[K] { flipColor(node) if isRed(node.left.left) { node = rotateRight(node) flipColor(node) } return node } func findMin[K constraints.Ordered](node *Node[K]) *Node[K] { if node == nil { return nil } for node.left != nil { node = node.left } return node } func findMax[K constraints.Ordered](node *Node[K]) *Node[K] { if node == nil { return nil } for node.right != nil { node = node.right } return node } func deleteMin[K constraints.Ordered](node *Node[K]) (*Node[K], *Node[K]) { if node.left == nil { // 3-nodes are left-leaning, so this is a leaf. return nil, node } if !isRed(node.left) && !isRed(node.left.left) { node = moveRedLeft(node) } var min *Node[K] node.left, min = deleteMin(node.left) return fixNode(node), min } func fixNode[K constraints.Ordered](node *Node[K]) *Node[K] { // rotate right red to left if isRed(node.right) { if LLRB234 { if isRed(node.right.left) { node.right = rotateRight(node.right) } } node = rotateLeft(node) } // rotate left red-red to right if isRed(node.left) && isRed(node.left.left) { node = rotateRight(node) } if !LLRB234 { // split 4-nodes if isRed(node.left) && isRed(node.right) { flipColor(node) } } return node } /************************************************************************* * Integrity checks ************************************************************************/ // checkRoot verifies that root property of the red-black tree is satisfied. func checkRoot[K constraints.Ordered](root *Node[K]) error { if isRed(root) { return fmt.Errorf("root property violation found") } return nil } // checkRed verifies that red property of the red-black tree is satisfied. func checkRed[K constraints.Ordered](node *Node[K]) error { if node == nil { return nil } if isRed(node) && (isRed(node.right) || isRed(node.left)) { return fmt.Errorf("red property violation found") } if err := checkRed(node.right); err != nil { return err } return checkRed(node.left) } // checkBlack verifies that black property of the red-black tree is satisfied. func checkBlack[K constraints.Ordered](node *Node[K], length *int) error { if node == nil { *length = 1 return nil } var rightLength int if err := checkBlack(node.right, &rightLength); err != nil { return err } var leftLength int if err := checkBlack(node.left, &leftLength); err != nil { return err } if rightLength != leftLength { return fmt.Errorf("black property violation found") } if !isRed(node) { *length = rightLength + 1 } else { *length = rightLength } return nil } // checkLLRB verifies that LLRB property of the left-leaning red-black tree is satisfied. func checkLLRB[K constraints.Ordered](node *Node[K]) error { if node == nil { return nil } if isRed(node.right) && !isRed(node.left) { return fmt.Errorf("LLRB property violation found") } if err := checkLLRB(node.right); err != nil { return err } return checkLLRB(node.left) } /************************************************************************* * Tree printing functions ************************************************************************/ type branchObj struct { prev *branchObj str string } func printBranch(branch *branchObj, out *bytes.Buffer) { if branch == nil { return } printBranch(branch.prev, out) out.WriteString(branch.str) } func printNode[K constraints.Ordered](node *Node[K], out *bytes.Buffer, pbranch *branchObj, right bool) { if node == nil { return } branch := &branchObj{ prev: pbranch, } if pbranch != nil { if right { branch.str = " " } else { branch.str = "│ " } } else { branch.str = "" } printNode(node.right, out, branch, true) printBranch(pbranch, out) if pbranch != nil { if right { out.WriteString("┌──") } else { out.WriteString("└──") } if node.red { out.WriteString("[") } else { out.WriteString("─") } } out.WriteString(fmt.Sprintf("%v", node.name)) if node.red { out.WriteString("]\n") } else { out.WriteString(" \n") } if pbranch != nil { if right { branch.str = "│ " } else { branch.str = " " } } else { branch.str = "" } printNode(node.left, out, branch, false) }