hashring/rbtree.go (224 lines of code) (raw):

// Copyright (c) 2015 Uber Technologies, Inc. // // 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. package hashring // redBlackTree is an implemantation of a Red Black Tree type redBlackTree struct { root *redBlackNode size int } type keytype interface { Compare(other interface{}) int } type valuetype interface{} // redBlackNode is a node of the redBlackTree type redBlackNode struct { key keytype value valuetype left *redBlackNode right *redBlackNode red bool } // Size returns the number of nodes in the redBlackTree func (t *redBlackTree) Size() int { return t.size } // Child returns the left or right node of the redBlackTree func (n *redBlackNode) Child(right bool) *redBlackNode { if right { return n.right } return n.left } func (n *redBlackNode) setChild(right bool, node *redBlackNode) { if right { n.right = node } else { n.left = node } } // returns true if redBlackNode is red func isRed(node *redBlackNode) bool { return node != nil && node.red } func singleRotate(oldroot *redBlackNode, dir bool) *redBlackNode { newroot := oldroot.Child(!dir) oldroot.setChild(!dir, newroot.Child(dir)) newroot.setChild(dir, oldroot) oldroot.red = true newroot.red = false return newroot } func doubleRotate(root *redBlackNode, dir bool) *redBlackNode { root.setChild(!dir, singleRotate(root.Child(!dir), !dir)) return singleRotate(root, dir) } // Insert inserts a value and string into the tree // Returns true on succesful insertion, false if duplicate exists func (t *redBlackTree) Insert(key keytype, value valuetype) (ret bool) { if t.root == nil { t.root = &redBlackNode{ key: key, value: value, } ret = true } else { var head = &redBlackNode{} var dir = true var last = true var parent *redBlackNode // parent var gparent *redBlackNode // grandparent var ggparent = head // great grandparent var node = t.root ggparent.right = t.root for { if node == nil { // insert new node at bottom node = &redBlackNode{ key: key, value: value, red: true, } parent.setChild(dir, node) ret = true } else if isRed(node.left) && isRed(node.right) { // flip colors node.red = true node.left.red, node.right.red = false, false } // fix red violation if isRed(node) && isRed(parent) { dir2 := ggparent.right == gparent if node == parent.Child(last) { ggparent.setChild(dir2, singleRotate(gparent, !last)) } else { ggparent.setChild(dir2, doubleRotate(gparent, !last)) } } cmp := node.key.Compare(key) // stop if found if cmp == 0 { break } last = dir dir = cmp < 0 // update helpers if gparent != nil { ggparent = gparent } gparent = parent parent = node node = node.Child(dir) } t.root = head.right } // make root black t.root.red = false if ret { t.size++ } return ret } // Delete removes the entry for key from the redBlackTree. Returns true on // succesful deletion, false if the key is not in tree func (t *redBlackTree) Delete(key keytype) bool { if t.root == nil { return false } var head = &redBlackNode{red: true} // fake red node to push down var node = head var parent *redBlackNode //parent var gparent *redBlackNode //grandparent var found *redBlackNode var dir = true node.right = t.root for node.Child(dir) != nil { last := dir // update helpers gparent = parent parent = node node = node.Child(dir) cmp := node.key.Compare(key) dir = cmp < 0 // save node if found if cmp == 0 { found = node } // pretend to push red node down if !isRed(node) && !isRed(node.Child(dir)) { if isRed(node.Child(!dir)) { sr := singleRotate(node, dir) parent.setChild(last, sr) parent = sr } else { sibling := parent.Child(!last) if sibling != nil { if !isRed(sibling.Child(!last)) && !isRed(sibling.Child(last)) { // flip colors parent.red = false sibling.red, node.red = true, true } else { dir2 := gparent.right == parent if isRed(sibling.Child(last)) { gparent.setChild(dir2, doubleRotate(parent, last)) } else if isRed(sibling.Child(!last)) { gparent.setChild(dir2, singleRotate(parent, last)) } gpc := gparent.Child(dir2) gpc.red = true node.red = true gpc.left.red, gpc.right.red = false, false } } } } } // get rid of node if we've found one if found != nil { found.key = node.key found.value = node.value parent.setChild(parent.right == node, node.Child(node.left == nil)) t.size-- } t.root = head.right if t.root != nil { t.root.red = false } return found != nil } func (n *redBlackNode) search(key keytype) (valuetype, bool) { cmp := n.key.Compare(key) if cmp == 0 { return n.value, true } else if 0 < cmp { if n.left != nil { return n.left.search(key) } } else if n.right != nil { return n.right.search(key) } return nil, false } // traverseWhile traverses the nodes in the tree in-order invoking the `condition`-function argument for each node. // If the condition-function returns `false` traversal is stopped and no more nodes will be // visited. Returns `true` if all nodes are visited; `false` if not. func (n *redBlackNode) traverseWhile(condition func(*redBlackNode) bool) bool { if n == nil { // the end of the tree does not signal the end of walking, but we can't // walk this node (nil) nor left or right anymore return true } // walk left first if !n.left.traverseWhile(condition) { // stop if walker indicated to break return false } // now visit this node if !condition(n) { // stop if walker indicated to break return false } // lastly visit right if !n.right.traverseWhile(condition) { // stop if walker indicated to break return false } // signal that we reached the end and walking should continue till we hit // end of tree return true } // Search searches for the entry for key in the redBlackTree, returns the value // and true if found or nil and false if there is no entry for key in the tree. func (t *redBlackTree) Search(key keytype) (valuetype, bool) { if t.root == nil { return nil, false } return t.root.search(key) } // LookupNUniqueAt iterates through the tree from the last node that is smaller // than key or equal, and returns the next n unique values. This function is not // guaranteed to return n values, less might be returned. Because this function // relies on writing the unique values to a golang map type, order cannot be // inferred. DEPRECATED, but maintained for backwards compatibility. func (t *redBlackTree) LookupNUniqueAt(n int, key keytype, result map[valuetype]struct{}) { findNUniqueAbove(t.root, n, key, result, nil) } // LookupOrderedNUniqueAt iterates through the tree from the last node that is // smaller than key or equal, and returns the next n unique values. This function // is not guaranteed to return n values, less might be returned. This method // replaces LookupNUniqueAt since it uses a slice to guarantee order. func (t *redBlackTree) LookupOrderedNUniqueAt(n int, key keytype, result map[valuetype]struct{}, orderedResult *[]valuetype) { findNUniqueAbove(t.root, n, key, result, orderedResult) } // findNUniqueAbove is a recursive search that finds n unique values with a key // bigger or equal than key func findNUniqueAbove(node *redBlackNode, n int, key keytype, result map[valuetype]struct{}, orderedResult *[]valuetype) { if len(result) >= n || node == nil { return } // skip left branch when all its keys are smaller than key cmp := node.key.Compare(key) if cmp >= 0 { findNUniqueAbove(node.left, n, key, result, orderedResult) } // Make sure to stop when we have n unique values if len(result) >= n { return } if cmp >= 0 { if _, ok := result[node.value]; !ok && orderedResult != nil { *orderedResult = append(*orderedResult, node.value) } result[node.value] = struct{}{} } findNUniqueAbove(node.right, n, key, result, orderedResult) }