From 8a4d7bd8736730d792a76cc64ca57f16671c0824 Mon Sep 17 00:00:00 2001 From: a Date: Sat, 24 Dec 2022 14:58:04 -0600 Subject: [PATCH 1/4] reorganize --- btreeg.go | 953 ------------------------------------------- btreeg_deprecated.go | 27 ++ btreeg_impl.go | 693 +++++++++++++++++++++++++++++++ btreeg_iter.go | 238 +++++++++++ 4 files changed, 958 insertions(+), 953 deletions(-) create mode 100644 btreeg_deprecated.go create mode 100644 btreeg_impl.go create mode 100644 btreeg_iter.go diff --git a/btreeg.go b/btreeg.go index 86b8990..fa65073 100644 --- a/btreeg.go +++ b/btreeg.go @@ -61,135 +61,12 @@ func NewBTreeGOptions[T any](less func(a, b T) bool, opts Options) *BTreeG[T] { return tr } -func (tr *BTreeG[T]) init(degree int) { - if tr.min != 0 { - return - } - tr.min, tr.max = degreeToMinMax(degree) - _, tr.copyItems = ((interface{})(tr.empty)).(copier[T]) - if !tr.copyItems { - _, tr.isoCopyItems = ((interface{})(tr.empty)).(isoCopier[T]) - } -} - // Less is a convenience function that performs a comparison of two items // using the same "less" function provided to New. func (tr *BTreeG[T]) Less(a, b T) bool { return tr.less(a, b) } -func (tr *BTreeG[T]) newNode(leaf bool) *node[T] { - n := &node[T]{isoid: tr.isoid} - if !leaf { - n.children = new([]*node[T]) - } - return n -} - -// leaf returns true if the node is a leaf. -func (n *node[T]) leaf() bool { - return n.children == nil -} - -func (tr *BTreeG[T]) bsearch(n *node[T], key T) (index int, found bool) { - low, high := 0, len(n.items) - for low < high { - h := (low + high) / 2 - if !tr.less(key, n.items[h]) { - low = h + 1 - } else { - high = h - } - } - if low > 0 && !tr.less(n.items[low-1], key) { - return low - 1, true - } - return low, false -} - -func (tr *BTreeG[T]) find(n *node[T], key T, hint *PathHint, depth int, -) (index int, found bool) { - if hint == nil { - return tr.bsearch(n, key) - } - return tr.hintsearch(n, key, hint, depth) -} - -func (tr *BTreeG[T]) hintsearch(n *node[T], key T, hint *PathHint, depth int, -) (index int, found bool) { - // Best case finds the exact match, updates the hint and returns. - // Worst case, updates the low and high bounds to binary search between. - low := 0 - high := len(n.items) - 1 - if depth < 8 && hint.used[depth] { - index = int(hint.path[depth]) - if index >= len(n.items) { - // tail item - if tr.Less(n.items[len(n.items)-1], key) { - index = len(n.items) - goto path_match - } - index = len(n.items) - 1 - } - if tr.Less(key, n.items[index]) { - if index == 0 || tr.Less(n.items[index-1], key) { - goto path_match - } - high = index - 1 - } else if tr.Less(n.items[index], key) { - low = index + 1 - } else { - found = true - goto path_match - } - } - - // Do a binary search between low and high - // keep on going until low > high, where the guarantee on low is that - // key >= items[low - 1] - for low <= high { - mid := low + ((high+1)-low)/2 - // if key >= n.items[mid], low = mid + 1 - // which implies that key >= everything below low - if !tr.Less(key, n.items[mid]) { - low = mid + 1 - } else { - high = mid - 1 - } - } - - // if low > 0, n.items[low - 1] >= key, - // we have from before that key >= n.items[low - 1] - // therefore key = n.items[low - 1], - // and we have found the entry for key. - // Otherwise we must keep searching for the key in index `low`. - if low > 0 && !tr.Less(n.items[low-1], key) { - index = low - 1 - found = true - } else { - index = low - found = false - } - -path_match: - if depth < 8 { - hint.used[depth] = true - var pathIndex uint8 - if n.leaf() && found { - pathIndex = uint8(index + 1) - } else { - pathIndex = uint8(index) - } - if pathIndex != hint.path[depth] { - hint.path[depth] = pathIndex - for i := depth + 1; i < 8; i++ { - hint.used[i] = false - } - } - } - return index, found -} - // SetHint sets or replace a value for a key using a path hint func (tr *BTreeG[T]) SetHint(item T, hint *PathHint) (prev T, replaced bool) { if tr.locks { @@ -202,151 +79,10 @@ func (tr *BTreeG[T]) SetHint(item T, hint *PathHint) (prev T, replaced bool) { return prev, replaced } -func (tr *BTreeG[T]) setHint(item T, hint *PathHint) (prev T, replaced bool) { - if tr.root == nil { - tr.init(0) - tr.root = tr.newNode(true) - tr.root.items = append([]T{}, item) - tr.root.count = 1 - tr.count = 1 - return tr.empty, false - } - prev, replaced, split := tr.nodeSet(&tr.root, item, hint, 0) - if split { - left := tr.isoLoad(&tr.root, true) - right, median := tr.nodeSplit(left) - tr.root = tr.newNode(false) - *tr.root.children = make([]*node[T], 0, tr.max+1) - *tr.root.children = append([]*node[T]{}, left, right) - tr.root.items = append([]T{}, median) - tr.root.updateCount() - return tr.setHint(item, hint) - } - if replaced { - return prev, true - } - tr.count++ - return tr.empty, false -} - // Set or replace a value for a key func (tr *BTreeG[T]) Set(item T) (T, bool) { return tr.SetHint(item, nil) } - -func (tr *BTreeG[T]) nodeSplit(n *node[T]) (right *node[T], median T) { - i := tr.max / 2 - median = n.items[i] - - // right node - right = tr.newNode(n.leaf()) - right.items = n.items[i+1:] - if !n.leaf() { - *right.children = (*n.children)[i+1:] - } - right.updateCount() - - // left node - n.items[i] = tr.empty - n.items = n.items[:i:i] - if !n.leaf() { - *n.children = (*n.children)[: i+1 : i+1] - } - n.updateCount() - - return right, median -} - -func (n *node[T]) updateCount() { - n.count = len(n.items) - if !n.leaf() { - for i := 0; i < len(*n.children); i++ { - n.count += (*n.children)[i].count - } - } -} - -// Copy the node for safe isolation. -func (tr *BTreeG[T]) copy(n *node[T]) *node[T] { - n2 := new(node[T]) - n2.isoid = tr.isoid - n2.count = n.count - n2.items = make([]T, len(n.items), cap(n.items)) - copy(n2.items, n.items) - if tr.copyItems { - for i := 0; i < len(n2.items); i++ { - n2.items[i] = ((interface{})(n2.items[i])).(copier[T]).Copy() - } - } else if tr.isoCopyItems { - for i := 0; i < len(n2.items); i++ { - n2.items[i] = ((interface{})(n2.items[i])).(isoCopier[T]).IsoCopy() - } - } - if !n.leaf() { - n2.children = new([]*node[T]) - *n2.children = make([]*node[T], len(*n.children), tr.max+1) - copy(*n2.children, *n.children) - } - return n2 -} - -// isoLoad loads the provided node and, if needed, performs a copy-on-write. -func (tr *BTreeG[T]) isoLoad(cn **node[T], mut bool) *node[T] { - if mut && (*cn).isoid != tr.isoid { - *cn = tr.copy(*cn) - } - return *cn -} - -func (tr *BTreeG[T]) nodeSet(cn **node[T], item T, - hint *PathHint, depth int, -) (prev T, replaced bool, split bool) { - if (*cn).isoid != tr.isoid { - *cn = tr.copy(*cn) - } - n := *cn - var i int - var found bool - if hint == nil { - i, found = tr.bsearch(n, item) - } else { - i, found = tr.hintsearch(n, item, hint, depth) - } - if found { - prev = n.items[i] - n.items[i] = item - return prev, true, false - } - if n.leaf() { - if len(n.items) == tr.max { - return tr.empty, false, true - } - n.items = append(n.items, tr.empty) - copy(n.items[i+1:], n.items[i:]) - n.items[i] = item - n.count++ - return tr.empty, false, false - } - prev, replaced, split = tr.nodeSet(&(*n.children)[i], item, hint, depth+1) - if split { - if len(n.items) == tr.max { - return tr.empty, false, true - } - right, median := tr.nodeSplit((*n.children)[i]) - *n.children = append(*n.children, nil) - copy((*n.children)[i+1:], (*n.children)[i:]) - (*n.children)[i+1] = right - n.items = append(n.items, tr.empty) - copy(n.items[i+1:], n.items[i:]) - n.items[i] = median - return tr.nodeSet(&n, item, hint, depth) - } - if !replaced { - n.count++ - } - return prev, replaced, false -} - func (tr *BTreeG[T]) Scan(iter func(item T) bool) { tr.scan(iter, false) } @@ -364,28 +100,6 @@ func (tr *BTreeG[T]) scan(iter func(item T) bool, mut bool) { tr.nodeScan(&tr.root, iter, mut) } -func (tr *BTreeG[T]) nodeScan(cn **node[T], iter func(item T) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - if n.leaf() { - for i := 0; i < len(n.items); i++ { - if !iter(n.items[i]) { - return false - } - } - return true - } - for i := 0; i < len(n.items); i++ { - if !tr.nodeScan(&(*n.children)[i], iter, mut) { - return false - } - if !iter(n.items[i]) { - return false - } - } - return tr.nodeScan(&(*n.children)[len(*n.children)-1], iter, mut) -} - // Get a value for key func (tr *BTreeG[T]) Get(key T) (T, bool) { return tr.getHint(key, nil, false) @@ -403,29 +117,6 @@ func (tr *BTreeG[T]) GetHintMut(key T, hint *PathHint) (value T, ok bool) { return tr.getHint(key, hint, true) } -// GetHint gets a value for key using a path hint -func (tr *BTreeG[T]) getHint(key T, hint *PathHint, mut bool) (T, bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return tr.empty, false - } - n := tr.isoLoad(&tr.root, mut) - depth := 0 - for { - i, found := tr.find(n, key, hint, depth) - if found { - return n.items[i], true - } - if n.children == nil { - return tr.empty, false - } - n = tr.isoLoad(&(*n.children)[i], mut) - depth++ - } -} - // Len returns the number of items in the tree func (tr *BTreeG[T]) Len() int { return tr.count @@ -447,156 +138,6 @@ func (tr *BTreeG[T]) DeleteHint(key T, hint *PathHint) (T, bool) { return tr.deleteHint(key, hint) } -func (tr *BTreeG[T]) deleteHint(key T, hint *PathHint) (T, bool) { - if tr.root == nil { - return tr.empty, false - } - prev, deleted := tr.delete(&tr.root, false, key, hint, 0) - if !deleted { - return tr.empty, false - } - if len(tr.root.items) == 0 && !tr.root.leaf() { - tr.root = (*tr.root.children)[0] - } - tr.count-- - if tr.count == 0 { - tr.root = nil - } - return prev, true -} - -func (tr *BTreeG[T]) delete(cn **node[T], max bool, key T, - hint *PathHint, depth int, -) (T, bool) { - n := tr.isoLoad(cn, true) - var i int - var found bool - if max { - i, found = len(n.items)-1, true - } else { - i, found = tr.find(n, key, hint, depth) - } - if n.leaf() { - if found { - // found the items at the leaf, remove it and return. - prev := n.items[i] - copy(n.items[i:], n.items[i+1:]) - n.items[len(n.items)-1] = tr.empty - n.items = n.items[:len(n.items)-1] - n.count-- - return prev, true - } - return tr.empty, false - } - - var prev T - var deleted bool - if found { - if max { - i++ - prev, deleted = tr.delete(&(*n.children)[i], true, tr.empty, nil, 0) - } else { - prev = n.items[i] - maxItem, _ := tr.delete(&(*n.children)[i], true, tr.empty, nil, 0) - deleted = true - n.items[i] = maxItem - } - } else { - prev, deleted = tr.delete(&(*n.children)[i], max, key, hint, depth+1) - } - if !deleted { - return tr.empty, false - } - n.count-- - if len((*n.children)[i].items) < tr.min { - tr.nodeRebalance(n, i) - } - return prev, true -} - -// nodeRebalance rebalances the child nodes following a delete operation. -// Provide the index of the child node with the number of items that fell -// below minItems. -func (tr *BTreeG[T]) nodeRebalance(n *node[T], i int) { - if i == len(n.items) { - i-- - } - - // ensure copy-on-write - left := tr.isoLoad(&(*n.children)[i], true) - right := tr.isoLoad(&(*n.children)[i+1], true) - - if len(left.items)+len(right.items) < tr.max { - // Merges the left and right children nodes together as a single node - // that includes (left,item,right), and places the contents into the - // existing left node. Delete the right node altogether and move the - // following items and child nodes to the left by one slot. - - // merge (left,item,right) - left.items = append(left.items, n.items[i]) - left.items = append(left.items, right.items...) - if !left.leaf() { - *left.children = append(*left.children, *right.children...) - } - left.count += right.count + 1 - - // move the items over one slot - copy(n.items[i:], n.items[i+1:]) - n.items[len(n.items)-1] = tr.empty - n.items = n.items[:len(n.items)-1] - - // move the children over one slot - copy((*n.children)[i+1:], (*n.children)[i+2:]) - (*n.children)[len(*n.children)-1] = nil - (*n.children) = (*n.children)[:len(*n.children)-1] - } else if len(left.items) > len(right.items) { - // move left -> right over one slot - - // Move the item of the parent node at index into the right-node first - // slot, and move the left-node last item into the previously moved - // parent item slot. - right.items = append(right.items, tr.empty) - copy(right.items[1:], right.items) - right.items[0] = n.items[i] - right.count++ - n.items[i] = left.items[len(left.items)-1] - left.items[len(left.items)-1] = tr.empty - left.items = left.items[:len(left.items)-1] - left.count-- - - if !left.leaf() { - // move the left-node last child into the right-node first slot - *right.children = append(*right.children, nil) - copy((*right.children)[1:], *right.children) - (*right.children)[0] = (*left.children)[len(*left.children)-1] - (*left.children)[len(*left.children)-1] = nil - (*left.children) = (*left.children)[:len(*left.children)-1] - left.count -= (*right.children)[0].count - right.count += (*right.children)[0].count - } - } else { - // move left <- right over one slot - - // Same as above but the other direction - left.items = append(left.items, n.items[i]) - left.count++ - n.items[i] = right.items[0] - copy(right.items, right.items[1:]) - right.items[len(right.items)-1] = tr.empty - right.items = right.items[:len(right.items)-1] - right.count-- - - if !left.leaf() { - *left.children = append(*left.children, (*right.children)[0]) - copy(*right.children, (*right.children)[1:]) - (*right.children)[len(*right.children)-1] = nil - *right.children = (*right.children)[:len(*right.children)-1] - left.count += (*left.children)[len(*left.children)-1].count - right.count -= (*left.children)[len(*left.children)-1].count - } - } -} - // Ascend the tree within the range [pivot, last] // Pass nil for pivot to scan all item in ascending order // Return false to stop iterating @@ -606,47 +147,6 @@ func (tr *BTreeG[T]) Ascend(pivot T, iter func(item T) bool) { func (tr *BTreeG[T]) AscendMut(pivot T, iter func(item T) bool) { tr.ascend(pivot, iter, true) } -func (tr *BTreeG[T]) ascend(pivot T, iter func(item T) bool, mut bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return - } - tr.nodeAscend(&tr.root, pivot, nil, 0, iter, mut) -} - -// The return value of this function determines whether we should keep iterating -// upon this functions return. -func (tr *BTreeG[T]) nodeAscend(cn **node[T], pivot T, hint *PathHint, - depth int, iter func(item T) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - i, found := tr.find(n, pivot, hint, depth) - if !found { - if !n.leaf() { - if !tr.nodeAscend(&(*n.children)[i], pivot, hint, depth+1, iter, - mut) { - return false - } - } - } - // We are either in the case that - // - node is found, we should iterate through it starting at `i`, - // the index it was located at. - // - node is not found, and TODO: fill in. - for ; i < len(n.items); i++ { - if !iter(n.items[i]) { - return false - } - if !n.leaf() { - if !tr.nodeScan(&(*n.children)[i+1], iter, mut) { - return false - } - } - } - return true -} func (tr *BTreeG[T]) Reverse(iter func(item T) bool) { tr.reverse(iter, false) @@ -664,31 +164,6 @@ func (tr *BTreeG[T]) reverse(iter func(item T) bool, mut bool) { tr.nodeReverse(&tr.root, iter, mut) } -func (tr *BTreeG[T]) nodeReverse(cn **node[T], iter func(item T) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - if n.leaf() { - for i := len(n.items) - 1; i >= 0; i-- { - if !iter(n.items[i]) { - return false - } - } - return true - } - if !tr.nodeReverse(&(*n.children)[len(*n.children)-1], iter, mut) { - return false - } - for i := len(n.items) - 1; i >= 0; i-- { - if !iter(n.items[i]) { - return false - } - if !tr.nodeReverse(&(*n.children)[i], iter, mut) { - return false - } - } - return true -} - // Descend the tree within the range [pivot, first] // Pass nil for pivot to scan all item in descending order // Return false to stop iterating @@ -708,33 +183,6 @@ func (tr *BTreeG[T]) descend(pivot T, iter func(item T) bool, mut bool) { tr.nodeDescend(&tr.root, pivot, nil, 0, iter, mut) } -func (tr *BTreeG[T]) nodeDescend(cn **node[T], pivot T, hint *PathHint, - depth int, iter func(item T) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - i, found := tr.find(n, pivot, hint, depth) - if !found { - if !n.leaf() { - if !tr.nodeDescend(&(*n.children)[i], pivot, hint, depth+1, iter, - mut) { - return false - } - } - i-- - } - for ; i >= 0; i-- { - if !iter(n.items[i]) { - return false - } - if !n.leaf() { - if !tr.nodeReverse(&(*n.children)[i], iter, mut) { - return false - } - } - } - return true -} - // Load is for bulk loading pre-sorted items func (tr *BTreeG[T]) Load(item T) (T, bool) { if tr.lock(true) { @@ -780,22 +228,6 @@ func (tr *BTreeG[T]) MinMut() (T, bool) { return tr.minMut(true) } -func (tr *BTreeG[T]) minMut(mut bool) (T, bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return tr.empty, false - } - n := tr.isoLoad(&tr.root, mut) - for { - if n.leaf() { - return n.items[0], true - } - n = tr.isoLoad(&(*n.children)[0], mut) - } -} - // Max returns the maximum item in tree. // Returns nil if the tree has no items. func (tr *BTreeG[T]) Max() (T, bool) { @@ -806,22 +238,6 @@ func (tr *BTreeG[T]) MaxMut() (T, bool) { return tr.maxMut(true) } -func (tr *BTreeG[T]) maxMut(mut bool) (T, bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return tr.empty, false - } - n := tr.isoLoad(&tr.root, mut) - for { - if n.leaf() { - return n.items[len(n.items)-1], true - } - n = tr.isoLoad(&(*n.children)[len(*n.children)-1], mut) - } -} - // PopMin removes the minimum item in tree and returns it. // Returns nil if the tree has no items. func (tr *BTreeG[T]) PopMin() (T, bool) { @@ -911,30 +327,6 @@ func (tr *BTreeG[T]) GetAt(index int) (T, bool) { func (tr *BTreeG[T]) GetAtMut(index int) (T, bool) { return tr.getAt(index, true) } -func (tr *BTreeG[T]) getAt(index int, mut bool) (T, bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil || index < 0 || index >= tr.count { - return tr.empty, false - } - n := tr.isoLoad(&tr.root, mut) - for { - if n.leaf() { - return n.items[index], true - } - i := 0 - for ; i < len(n.items); i++ { - if index < (*n.children)[i].count { - break - } else if index == (*n.children)[i].count { - return n.items[i], true - } - index -= (*n.children)[i].count + 1 - } - n = tr.isoLoad(&(*n.children)[i], mut) - } -} // DeleteAt deletes the item at index. // Return nil if the tree is empty or the index is out of bounds. @@ -1026,38 +418,6 @@ func (tr *BTreeG[T]) Walk(iter func(item []T) bool) { func (tr *BTreeG[T]) WalkMut(iter func(item []T) bool) { tr.walk(iter, true) } -func (tr *BTreeG[T]) walk(iter func(item []T) bool, mut bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return - } - tr.nodeWalk(&tr.root, iter, mut) -} - -func (tr *BTreeG[T]) nodeWalk(cn **node[T], iter func(item []T) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - if n.leaf() { - if !iter(n.items) { - return false - } - } else { - for i := 0; i < len(n.items); i++ { - if !tr.nodeWalk(&(*n.children)[i], iter, mut) { - return false - } - if !iter(n.items[i : i+1]) { - return false - } - } - if !tr.nodeWalk(&(*n.children)[len(n.items)], iter, mut) { - return false - } - } - return true -} // Copy the tree. This is a copy-on-write operation and is very fast because // it only performs a shadowed copy. @@ -1076,316 +436,3 @@ func (tr *BTreeG[T]) IsoCopy() *BTreeG[T] { tr2.isoid = newIsoID() return tr2 } - -func (tr *BTreeG[T]) lock(write bool) bool { - if tr.locks { - if write { - tr.mu.Lock() - } else { - tr.mu.RLock() - } - } - return tr.locks -} - -func (tr *BTreeG[T]) unlock(write bool) { - if write { - tr.mu.Unlock() - } else { - tr.mu.RUnlock() - } -} - -// Iter represents an iterator -type IterG[T any] struct { - tr *BTreeG[T] - mut bool - locked bool - seeked bool - atstart bool - atend bool - stack []iterStackItemG[T] - item T -} - -type iterStackItemG[T any] struct { - n *node[T] - i int -} - -// Iter returns a read-only iterator. -// The Release method must be called finished with iterator. -func (tr *BTreeG[T]) Iter() IterG[T] { - return tr.iter(false) -} - -func (tr *BTreeG[T]) IterMut() IterG[T] { - return tr.iter(true) -} - -func (tr *BTreeG[T]) iter(mut bool) IterG[T] { - var iter IterG[T] - iter.tr = tr - iter.mut = mut - iter.locked = tr.lock(iter.mut) - return iter -} - -// Seek to item greater-or-equal-to key. -// Returns false if there was no item found. -func (iter *IterG[T]) Seek(key T) bool { - if iter.tr == nil { - return false - } - iter.seeked = true - iter.stack = iter.stack[:0] - if iter.tr.root == nil { - return false - } - n := iter.tr.isoLoad(&iter.tr.root, iter.mut) - for { - i, found := iter.tr.find(n, key, nil, 0) - iter.stack = append(iter.stack, iterStackItemG[T]{n, i}) - if found { - iter.item = n.items[i] - return true - } - if n.leaf() { - iter.stack[len(iter.stack)-1].i-- - return iter.Next() - } - n = iter.tr.isoLoad(&(*n.children)[i], iter.mut) - } -} - -// First moves iterator to first item in tree. -// Returns false if the tree is empty. -func (iter *IterG[T]) First() bool { - if iter.tr == nil { - return false - } - iter.atend = false - iter.atstart = false - iter.seeked = true - iter.stack = iter.stack[:0] - if iter.tr.root == nil { - return false - } - n := iter.tr.isoLoad(&iter.tr.root, iter.mut) - for { - iter.stack = append(iter.stack, iterStackItemG[T]{n, 0}) - if n.leaf() { - break - } - n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) - } - s := &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Last moves iterator to last item in tree. -// Returns false if the tree is empty. -func (iter *IterG[T]) Last() bool { - if iter.tr == nil { - return false - } - iter.seeked = true - iter.stack = iter.stack[:0] - if iter.tr.root == nil { - return false - } - n := iter.tr.isoLoad(&iter.tr.root, iter.mut) - for { - iter.stack = append(iter.stack, iterStackItemG[T]{n, len(n.items)}) - if n.leaf() { - iter.stack[len(iter.stack)-1].i-- - break - } - n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) - } - s := &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Release the iterator. -func (iter *IterG[T]) Release() { - if iter.tr == nil { - return - } - if iter.locked { - iter.tr.unlock(iter.mut) - iter.locked = false - } - iter.stack = nil - iter.tr = nil -} - -// Next moves iterator to the next item in iterator. -// Returns false if the tree is empty or the iterator is at the end of -// the tree. -func (iter *IterG[T]) Next() bool { - if iter.tr == nil { - return false - } - if !iter.seeked { - return iter.First() - } - if len(iter.stack) == 0 { - if iter.atstart { - return iter.First() && iter.Next() - } - return false - } - s := &iter.stack[len(iter.stack)-1] - s.i++ - if s.n.leaf() { - if s.i == len(s.n.items) { - for { - iter.stack = iter.stack[:len(iter.stack)-1] - if len(iter.stack) == 0 { - iter.atend = true - return false - } - s = &iter.stack[len(iter.stack)-1] - if s.i < len(s.n.items) { - break - } - } - } - } else { - n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) - for { - iter.stack = append(iter.stack, iterStackItemG[T]{n, 0}) - if n.leaf() { - break - } - n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) - } - } - s = &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Prev moves iterator to the previous item in iterator. -// Returns false if the tree is empty or the iterator is at the beginning of -// the tree. -func (iter *IterG[T]) Prev() bool { - if iter.tr == nil { - return false - } - if !iter.seeked { - return false - } - if len(iter.stack) == 0 { - if iter.atend { - return iter.Last() && iter.Prev() - } - return false - } - s := &iter.stack[len(iter.stack)-1] - if s.n.leaf() { - s.i-- - if s.i == -1 { - for { - iter.stack = iter.stack[:len(iter.stack)-1] - if len(iter.stack) == 0 { - iter.atstart = true - return false - } - s = &iter.stack[len(iter.stack)-1] - s.i-- - if s.i > -1 { - break - } - } - } - } else { - n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) - for { - iter.stack = append(iter.stack, iterStackItemG[T]{n, len(n.items)}) - if n.leaf() { - iter.stack[len(iter.stack)-1].i-- - break - } - n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) - } - } - s = &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Item returns the current iterator item. -func (iter *IterG[T]) Item() T { - return iter.item -} - -// Items returns all the items in order. -func (tr *BTreeG[T]) Items() []T { - return tr.items(false) -} - -func (tr *BTreeG[T]) ItemsMut() []T { - return tr.items(true) -} - -func (tr *BTreeG[T]) items(mut bool) []T { - if tr.lock(mut) { - defer tr.unlock(mut) - } - items := make([]T, 0, tr.Len()) - if tr.root != nil { - items = tr.nodeItems(&tr.root, items, mut) - } - return items -} - -func (tr *BTreeG[T]) nodeItems(cn **node[T], items []T, mut bool) []T { - n := tr.isoLoad(cn, mut) - if n.leaf() { - return append(items, n.items...) - } - for i := 0; i < len(n.items); i++ { - items = tr.nodeItems(&(*n.children)[i], items, mut) - items = append(items, n.items[i]) - } - return tr.nodeItems(&(*n.children)[len(*n.children)-1], items, mut) -} - -// Clear will delete all items. -func (tr *BTreeG[T]) Clear() { - if tr.lock(true) { - defer tr.unlock(true) - } - tr.root = nil - tr.count = 0 -} - -// Generic BTree -// -// Deprecated: use BTreeG -type Generic[T any] struct { - *BTreeG[T] -} - -// NewGeneric returns a generic BTree -// -// Deprecated: use NewBTreeG -func NewGeneric[T any](less func(a, b T) bool) *Generic[T] { - return &Generic[T]{NewBTreeGOptions(less, Options{})} -} - -// NewGenericOptions returns a generic BTree -// -// Deprecated: use NewBTreeGOptions -func NewGenericOptions[T any](less func(a, b T) bool, opts Options, -) *Generic[T] { - return &Generic[T]{NewBTreeGOptions(less, opts)} -} - -func (tr *Generic[T]) Copy() *Generic[T] { - return &Generic[T]{tr.BTreeG.Copy()} -} diff --git a/btreeg_deprecated.go b/btreeg_deprecated.go new file mode 100644 index 0000000..72cfd59 --- /dev/null +++ b/btreeg_deprecated.go @@ -0,0 +1,27 @@ +package btree + +// Generic BTree +// +// Deprecated: use BTreeG +type Generic[T any] struct { + *BTreeG[T] +} + +// NewGeneric returns a generic BTree +// +// Deprecated: use NewBTreeG +func NewGeneric[T any](less func(a, b T) bool) *Generic[T] { + return &Generic[T]{NewBTreeGOptions(less, Options{})} +} + +// NewGenericOptions returns a generic BTree +// +// Deprecated: use NewBTreeGOptions +func NewGenericOptions[T any](less func(a, b T) bool, opts Options, +) *Generic[T] { + return &Generic[T]{NewBTreeGOptions(less, opts)} +} + +func (tr *Generic[T]) Copy() *Generic[T] { + return &Generic[T]{tr.BTreeG.Copy()} +} diff --git a/btreeg_impl.go b/btreeg_impl.go new file mode 100644 index 0000000..ee36a98 --- /dev/null +++ b/btreeg_impl.go @@ -0,0 +1,693 @@ +package btree + +func (tr *BTreeG[T]) init(degree int) { + if tr.min != 0 { + return + } + tr.min, tr.max = degreeToMinMax(degree) + _, tr.copyItems = ((interface{})(tr.empty)).(copier[T]) + if !tr.copyItems { + _, tr.isoCopyItems = ((interface{})(tr.empty)).(isoCopier[T]) + } +} + +func (tr *BTreeG[T]) newNode(leaf bool) *node[T] { + n := &node[T]{isoid: tr.isoid} + if !leaf { + n.children = new([]*node[T]) + } + return n +} + +// leaf returns true if the node is a leaf. +func (n *node[T]) leaf() bool { + return n.children == nil +} + +func (tr *BTreeG[T]) bsearch(n *node[T], key T) (index int, found bool) { + low, high := 0, len(n.items) + for low < high { + h := (low + high) / 2 + if !tr.less(key, n.items[h]) { + low = h + 1 + } else { + high = h + } + } + if low > 0 && !tr.less(n.items[low-1], key) { + return low - 1, true + } + return low, false +} + +func (tr *BTreeG[T]) find(n *node[T], key T, hint *PathHint, depth int, +) (index int, found bool) { + if hint == nil { + return tr.bsearch(n, key) + } + return tr.hintsearch(n, key, hint, depth) +} + +func (tr *BTreeG[T]) hintsearch(n *node[T], key T, hint *PathHint, depth int, +) (index int, found bool) { + // Best case finds the exact match, updates the hint and returns. + // Worst case, updates the low and high bounds to binary search between. + low := 0 + high := len(n.items) - 1 + if depth < 8 && hint.used[depth] { + index = int(hint.path[depth]) + if index >= len(n.items) { + // tail item + if tr.Less(n.items[len(n.items)-1], key) { + index = len(n.items) + goto path_match + } + index = len(n.items) - 1 + } + if tr.Less(key, n.items[index]) { + if index == 0 || tr.Less(n.items[index-1], key) { + goto path_match + } + high = index - 1 + } else if tr.Less(n.items[index], key) { + low = index + 1 + } else { + found = true + goto path_match + } + } + + // Do a binary search between low and high + // keep on going until low > high, where the guarantee on low is that + // key >= items[low - 1] + for low <= high { + mid := low + ((high+1)-low)/2 + // if key >= n.items[mid], low = mid + 1 + // which implies that key >= everything below low + if !tr.Less(key, n.items[mid]) { + low = mid + 1 + } else { + high = mid - 1 + } + } + + // if low > 0, n.items[low - 1] >= key, + // we have from before that key >= n.items[low - 1] + // therefore key = n.items[low - 1], + // and we have found the entry for key. + // Otherwise we must keep searching for the key in index `low`. + if low > 0 && !tr.Less(n.items[low-1], key) { + index = low - 1 + found = true + } else { + index = low + found = false + } + +path_match: + if depth < 8 { + hint.used[depth] = true + var pathIndex uint8 + if n.leaf() && found { + pathIndex = uint8(index + 1) + } else { + pathIndex = uint8(index) + } + if pathIndex != hint.path[depth] { + hint.path[depth] = pathIndex + for i := depth + 1; i < 8; i++ { + hint.used[i] = false + } + } + } + return index, found +} + +func (tr *BTreeG[T]) setHint(item T, hint *PathHint) (prev T, replaced bool) { + if tr.root == nil { + tr.init(0) + tr.root = tr.newNode(true) + tr.root.items = append([]T{}, item) + tr.root.count = 1 + tr.count = 1 + return tr.empty, false + } + prev, replaced, split := tr.nodeSet(&tr.root, item, hint, 0) + if split { + left := tr.isoLoad(&tr.root, true) + right, median := tr.nodeSplit(left) + tr.root = tr.newNode(false) + *tr.root.children = make([]*node[T], 0, tr.max+1) + *tr.root.children = append([]*node[T]{}, left, right) + tr.root.items = append([]T{}, median) + tr.root.updateCount() + return tr.setHint(item, hint) + } + if replaced { + return prev, true + } + tr.count++ + return tr.empty, false +} + +func (tr *BTreeG[T]) nodeSplit(n *node[T]) (right *node[T], median T) { + i := tr.max / 2 + median = n.items[i] + + // right node + right = tr.newNode(n.leaf()) + right.items = n.items[i+1:] + if !n.leaf() { + *right.children = (*n.children)[i+1:] + } + right.updateCount() + + // left node + n.items[i] = tr.empty + n.items = n.items[:i:i] + if !n.leaf() { + *n.children = (*n.children)[: i+1 : i+1] + } + n.updateCount() + + return right, median +} + +func (n *node[T]) updateCount() { + n.count = len(n.items) + if !n.leaf() { + for i := 0; i < len(*n.children); i++ { + n.count += (*n.children)[i].count + } + } +} + +// Copy the node for safe isolation. +func (tr *BTreeG[T]) copy(n *node[T]) *node[T] { + n2 := new(node[T]) + n2.isoid = tr.isoid + n2.count = n.count + n2.items = make([]T, len(n.items), cap(n.items)) + copy(n2.items, n.items) + if tr.copyItems { + for i := 0; i < len(n2.items); i++ { + n2.items[i] = ((interface{})(n2.items[i])).(copier[T]).Copy() + } + } else if tr.isoCopyItems { + for i := 0; i < len(n2.items); i++ { + n2.items[i] = ((interface{})(n2.items[i])).(isoCopier[T]).IsoCopy() + } + } + if !n.leaf() { + n2.children = new([]*node[T]) + *n2.children = make([]*node[T], len(*n.children), tr.max+1) + copy(*n2.children, *n.children) + } + return n2 +} + +// isoLoad loads the provided node and, if needed, performs a copy-on-write. +func (tr *BTreeG[T]) isoLoad(cn **node[T], mut bool) *node[T] { + if mut && (*cn).isoid != tr.isoid { + *cn = tr.copy(*cn) + } + return *cn +} + +func (tr *BTreeG[T]) nodeSet(cn **node[T], item T, + hint *PathHint, depth int, +) (prev T, replaced bool, split bool) { + if (*cn).isoid != tr.isoid { + *cn = tr.copy(*cn) + } + n := *cn + var i int + var found bool + if hint == nil { + i, found = tr.bsearch(n, item) + } else { + i, found = tr.hintsearch(n, item, hint, depth) + } + if found { + prev = n.items[i] + n.items[i] = item + return prev, true, false + } + if n.leaf() { + if len(n.items) == tr.max { + return tr.empty, false, true + } + n.items = append(n.items, tr.empty) + copy(n.items[i+1:], n.items[i:]) + n.items[i] = item + n.count++ + return tr.empty, false, false + } + prev, replaced, split = tr.nodeSet(&(*n.children)[i], item, hint, depth+1) + if split { + if len(n.items) == tr.max { + return tr.empty, false, true + } + right, median := tr.nodeSplit((*n.children)[i]) + *n.children = append(*n.children, nil) + copy((*n.children)[i+1:], (*n.children)[i:]) + (*n.children)[i+1] = right + n.items = append(n.items, tr.empty) + copy(n.items[i+1:], n.items[i:]) + n.items[i] = median + return tr.nodeSet(&n, item, hint, depth) + } + if !replaced { + n.count++ + } + return prev, replaced, false +} + +func (tr *BTreeG[T]) nodeScan(cn **node[T], iter func(item T) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + if n.leaf() { + for i := 0; i < len(n.items); i++ { + if !iter(n.items[i]) { + return false + } + } + return true + } + for i := 0; i < len(n.items); i++ { + if !tr.nodeScan(&(*n.children)[i], iter, mut) { + return false + } + if !iter(n.items[i]) { + return false + } + } + return tr.nodeScan(&(*n.children)[len(*n.children)-1], iter, mut) +} + +// GetHint gets a value for key using a path hint +func (tr *BTreeG[T]) getHint(key T, hint *PathHint, mut bool) (T, bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return tr.empty, false + } + n := tr.isoLoad(&tr.root, mut) + depth := 0 + for { + i, found := tr.find(n, key, hint, depth) + if found { + return n.items[i], true + } + if n.children == nil { + return tr.empty, false + } + n = tr.isoLoad(&(*n.children)[i], mut) + depth++ + } +} + +func (tr *BTreeG[T]) deleteHint(key T, hint *PathHint) (T, bool) { + if tr.root == nil { + return tr.empty, false + } + prev, deleted := tr.delete(&tr.root, false, key, hint, 0) + if !deleted { + return tr.empty, false + } + if len(tr.root.items) == 0 && !tr.root.leaf() { + tr.root = (*tr.root.children)[0] + } + tr.count-- + if tr.count == 0 { + tr.root = nil + } + return prev, true +} + +func (tr *BTreeG[T]) delete(cn **node[T], max bool, key T, + hint *PathHint, depth int, +) (T, bool) { + n := tr.isoLoad(cn, true) + var i int + var found bool + if max { + i, found = len(n.items)-1, true + } else { + i, found = tr.find(n, key, hint, depth) + } + if n.leaf() { + if found { + // found the items at the leaf, remove it and return. + prev := n.items[i] + copy(n.items[i:], n.items[i+1:]) + n.items[len(n.items)-1] = tr.empty + n.items = n.items[:len(n.items)-1] + n.count-- + return prev, true + } + return tr.empty, false + } + + var prev T + var deleted bool + if found { + if max { + i++ + prev, deleted = tr.delete(&(*n.children)[i], true, tr.empty, nil, 0) + } else { + prev = n.items[i] + maxItem, _ := tr.delete(&(*n.children)[i], true, tr.empty, nil, 0) + deleted = true + n.items[i] = maxItem + } + } else { + prev, deleted = tr.delete(&(*n.children)[i], max, key, hint, depth+1) + } + if !deleted { + return tr.empty, false + } + n.count-- + if len((*n.children)[i].items) < tr.min { + tr.nodeRebalance(n, i) + } + return prev, true +} + +// nodeRebalance rebalances the child nodes following a delete operation. +// Provide the index of the child node with the number of items that fell +// below minItems. +func (tr *BTreeG[T]) nodeRebalance(n *node[T], i int) { + if i == len(n.items) { + i-- + } + + // ensure copy-on-write + left := tr.isoLoad(&(*n.children)[i], true) + right := tr.isoLoad(&(*n.children)[i+1], true) + + if len(left.items)+len(right.items) < tr.max { + // Merges the left and right children nodes together as a single node + // that includes (left,item,right), and places the contents into the + // existing left node. Delete the right node altogether and move the + // following items and child nodes to the left by one slot. + + // merge (left,item,right) + left.items = append(left.items, n.items[i]) + left.items = append(left.items, right.items...) + if !left.leaf() { + *left.children = append(*left.children, *right.children...) + } + left.count += right.count + 1 + + // move the items over one slot + copy(n.items[i:], n.items[i+1:]) + n.items[len(n.items)-1] = tr.empty + n.items = n.items[:len(n.items)-1] + + // move the children over one slot + copy((*n.children)[i+1:], (*n.children)[i+2:]) + (*n.children)[len(*n.children)-1] = nil + (*n.children) = (*n.children)[:len(*n.children)-1] + } else if len(left.items) > len(right.items) { + // move left -> right over one slot + + // Move the item of the parent node at index into the right-node first + // slot, and move the left-node last item into the previously moved + // parent item slot. + right.items = append(right.items, tr.empty) + copy(right.items[1:], right.items) + right.items[0] = n.items[i] + right.count++ + n.items[i] = left.items[len(left.items)-1] + left.items[len(left.items)-1] = tr.empty + left.items = left.items[:len(left.items)-1] + left.count-- + + if !left.leaf() { + // move the left-node last child into the right-node first slot + *right.children = append(*right.children, nil) + copy((*right.children)[1:], *right.children) + (*right.children)[0] = (*left.children)[len(*left.children)-1] + (*left.children)[len(*left.children)-1] = nil + (*left.children) = (*left.children)[:len(*left.children)-1] + left.count -= (*right.children)[0].count + right.count += (*right.children)[0].count + } + } else { + // move left <- right over one slot + + // Same as above but the other direction + left.items = append(left.items, n.items[i]) + left.count++ + n.items[i] = right.items[0] + copy(right.items, right.items[1:]) + right.items[len(right.items)-1] = tr.empty + right.items = right.items[:len(right.items)-1] + right.count-- + + if !left.leaf() { + *left.children = append(*left.children, (*right.children)[0]) + copy(*right.children, (*right.children)[1:]) + (*right.children)[len(*right.children)-1] = nil + *right.children = (*right.children)[:len(*right.children)-1] + left.count += (*left.children)[len(*left.children)-1].count + right.count -= (*left.children)[len(*left.children)-1].count + } + } +} + +func (tr *BTreeG[T]) ascend(pivot T, iter func(item T) bool, mut bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return + } + tr.nodeAscend(&tr.root, pivot, nil, 0, iter, mut) +} + +// The return value of this function determines whether we should keep iterating +// upon this functions return. +func (tr *BTreeG[T]) nodeAscend(cn **node[T], pivot T, hint *PathHint, + depth int, iter func(item T) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + i, found := tr.find(n, pivot, hint, depth) + if !found { + if !n.leaf() { + if !tr.nodeAscend(&(*n.children)[i], pivot, hint, depth+1, iter, + mut) { + return false + } + } + } + // We are either in the case that + // - node is found, we should iterate through it starting at `i`, + // the index it was located at. + // - node is not found, and TODO: fill in. + for ; i < len(n.items); i++ { + if !iter(n.items[i]) { + return false + } + if !n.leaf() { + if !tr.nodeScan(&(*n.children)[i+1], iter, mut) { + return false + } + } + } + return true +} + +func (tr *BTreeG[T]) nodeReverse(cn **node[T], iter func(item T) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + if n.leaf() { + for i := len(n.items) - 1; i >= 0; i-- { + if !iter(n.items[i]) { + return false + } + } + return true + } + if !tr.nodeReverse(&(*n.children)[len(*n.children)-1], iter, mut) { + return false + } + for i := len(n.items) - 1; i >= 0; i-- { + if !iter(n.items[i]) { + return false + } + if !tr.nodeReverse(&(*n.children)[i], iter, mut) { + return false + } + } + return true +} + +func (tr *BTreeG[T]) nodeDescend(cn **node[T], pivot T, hint *PathHint, + depth int, iter func(item T) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + i, found := tr.find(n, pivot, hint, depth) + if !found { + if !n.leaf() { + if !tr.nodeDescend(&(*n.children)[i], pivot, hint, depth+1, iter, + mut) { + return false + } + } + i-- + } + for ; i >= 0; i-- { + if !iter(n.items[i]) { + return false + } + if !n.leaf() { + if !tr.nodeReverse(&(*n.children)[i], iter, mut) { + return false + } + } + } + return true +} + +func (tr *BTreeG[T]) minMut(mut bool) (T, bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return tr.empty, false + } + n := tr.isoLoad(&tr.root, mut) + for { + if n.leaf() { + return n.items[0], true + } + n = tr.isoLoad(&(*n.children)[0], mut) + } +} + +func (tr *BTreeG[T]) maxMut(mut bool) (T, bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return tr.empty, false + } + n := tr.isoLoad(&tr.root, mut) + for { + if n.leaf() { + return n.items[len(n.items)-1], true + } + n = tr.isoLoad(&(*n.children)[len(*n.children)-1], mut) + } +} + +func (tr *BTreeG[T]) getAt(index int, mut bool) (T, bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil || index < 0 || index >= tr.count { + return tr.empty, false + } + n := tr.isoLoad(&tr.root, mut) + for { + if n.leaf() { + return n.items[index], true + } + i := 0 + for ; i < len(n.items); i++ { + if index < (*n.children)[i].count { + break + } else if index == (*n.children)[i].count { + return n.items[i], true + } + index -= (*n.children)[i].count + 1 + } + n = tr.isoLoad(&(*n.children)[i], mut) + } +} + +func (tr *BTreeG[T]) walk(iter func(item []T) bool, mut bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return + } + tr.nodeWalk(&tr.root, iter, mut) +} + +func (tr *BTreeG[T]) nodeWalk(cn **node[T], iter func(item []T) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + if n.leaf() { + if !iter(n.items) { + return false + } + } else { + for i := 0; i < len(n.items); i++ { + if !tr.nodeWalk(&(*n.children)[i], iter, mut) { + return false + } + if !iter(n.items[i : i+1]) { + return false + } + } + if !tr.nodeWalk(&(*n.children)[len(n.items)], iter, mut) { + return false + } + } + return true +} + +func (tr *BTreeG[T]) lock(write bool) bool { + if tr.locks { + if write { + tr.mu.Lock() + } else { + tr.mu.RLock() + } + } + return tr.locks +} + +func (tr *BTreeG[T]) unlock(write bool) { + if write { + tr.mu.Unlock() + } else { + tr.mu.RUnlock() + } +} + +func (tr *BTreeG[T]) iter(mut bool) IterG[T] { + var iter IterG[T] + iter.tr = tr + iter.mut = mut + iter.locked = tr.lock(iter.mut) + return iter +} + +func (tr *BTreeG[T]) items(mut bool) []T { + if tr.lock(mut) { + defer tr.unlock(mut) + } + items := make([]T, 0, tr.Len()) + if tr.root != nil { + items = tr.nodeItems(&tr.root, items, mut) + } + return items +} + +func (tr *BTreeG[T]) nodeItems(cn **node[T], items []T, mut bool) []T { + n := tr.isoLoad(cn, mut) + if n.leaf() { + return append(items, n.items...) + } + for i := 0; i < len(n.items); i++ { + items = tr.nodeItems(&(*n.children)[i], items, mut) + items = append(items, n.items[i]) + } + return tr.nodeItems(&(*n.children)[len(*n.children)-1], items, mut) +} diff --git a/btreeg_iter.go b/btreeg_iter.go new file mode 100644 index 0000000..0d78570 --- /dev/null +++ b/btreeg_iter.go @@ -0,0 +1,238 @@ +package btree + +// Iter represents an iterator +type IterG[T any] struct { + tr *BTreeG[T] + mut bool + locked bool + seeked bool + atstart bool + atend bool + stack []iterStackItemG[T] + item T +} + +type iterStackItemG[T any] struct { + n *node[T] + i int +} + +// Iter returns a read-only iterator. +// The Release method must be called finished with iterator. +func (tr *BTreeG[T]) Iter() IterG[T] { + return tr.iter(false) +} + +func (tr *BTreeG[T]) IterMut() IterG[T] { + return tr.iter(true) +} + +// Seek to item greater-or-equal-to key. +// Returns false if there was no item found. +func (iter *IterG[T]) Seek(key T) bool { + if iter.tr == nil { + return false + } + iter.seeked = true + iter.stack = iter.stack[:0] + if iter.tr.root == nil { + return false + } + n := iter.tr.isoLoad(&iter.tr.root, iter.mut) + for { + i, found := iter.tr.find(n, key, nil, 0) + iter.stack = append(iter.stack, iterStackItemG[T]{n, i}) + if found { + iter.item = n.items[i] + return true + } + if n.leaf() { + iter.stack[len(iter.stack)-1].i-- + return iter.Next() + } + n = iter.tr.isoLoad(&(*n.children)[i], iter.mut) + } +} + +// First moves iterator to first item in tree. +// Returns false if the tree is empty. +func (iter *IterG[T]) First() bool { + if iter.tr == nil { + return false + } + iter.atend = false + iter.atstart = false + iter.seeked = true + iter.stack = iter.stack[:0] + if iter.tr.root == nil { + return false + } + n := iter.tr.isoLoad(&iter.tr.root, iter.mut) + for { + iter.stack = append(iter.stack, iterStackItemG[T]{n, 0}) + if n.leaf() { + break + } + n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) + } + s := &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Last moves iterator to last item in tree. +// Returns false if the tree is empty. +func (iter *IterG[T]) Last() bool { + if iter.tr == nil { + return false + } + iter.seeked = true + iter.stack = iter.stack[:0] + if iter.tr.root == nil { + return false + } + n := iter.tr.isoLoad(&iter.tr.root, iter.mut) + for { + iter.stack = append(iter.stack, iterStackItemG[T]{n, len(n.items)}) + if n.leaf() { + iter.stack[len(iter.stack)-1].i-- + break + } + n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) + } + s := &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Release the iterator. +func (iter *IterG[T]) Release() { + if iter.tr == nil { + return + } + if iter.locked { + iter.tr.unlock(iter.mut) + iter.locked = false + } + iter.stack = nil + iter.tr = nil +} + +// Next moves iterator to the next item in iterator. +// Returns false if the tree is empty or the iterator is at the end of +// the tree. +func (iter *IterG[T]) Next() bool { + if iter.tr == nil { + return false + } + if !iter.seeked { + return iter.First() + } + if len(iter.stack) == 0 { + if iter.atstart { + return iter.First() && iter.Next() + } + return false + } + s := &iter.stack[len(iter.stack)-1] + s.i++ + if s.n.leaf() { + if s.i == len(s.n.items) { + for { + iter.stack = iter.stack[:len(iter.stack)-1] + if len(iter.stack) == 0 { + iter.atend = true + return false + } + s = &iter.stack[len(iter.stack)-1] + if s.i < len(s.n.items) { + break + } + } + } + } else { + n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) + for { + iter.stack = append(iter.stack, iterStackItemG[T]{n, 0}) + if n.leaf() { + break + } + n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) + } + } + s = &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Prev moves iterator to the previous item in iterator. +// Returns false if the tree is empty or the iterator is at the beginning of +// the tree. +func (iter *IterG[T]) Prev() bool { + if iter.tr == nil { + return false + } + if !iter.seeked { + return false + } + if len(iter.stack) == 0 { + if iter.atend { + return iter.Last() && iter.Prev() + } + return false + } + s := &iter.stack[len(iter.stack)-1] + if s.n.leaf() { + s.i-- + if s.i == -1 { + for { + iter.stack = iter.stack[:len(iter.stack)-1] + if len(iter.stack) == 0 { + iter.atstart = true + return false + } + s = &iter.stack[len(iter.stack)-1] + s.i-- + if s.i > -1 { + break + } + } + } + } else { + n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) + for { + iter.stack = append(iter.stack, iterStackItemG[T]{n, len(n.items)}) + if n.leaf() { + iter.stack[len(iter.stack)-1].i-- + break + } + n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) + } + } + s = &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Item returns the current iterator item. +func (iter *IterG[T]) Item() T { + return iter.item +} + +// Items returns all the items in order. +func (tr *BTreeG[T]) Items() []T { + return tr.items(false) +} + +func (tr *BTreeG[T]) ItemsMut() []T { + return tr.items(true) +} + +// Clear will delete all items. +func (tr *BTreeG[T]) Clear() { + if tr.lock(true) { + defer tr.unlock(true) + } + tr.root = nil + tr.count = 0 +} From 22c370445e1ab8069d5c3fced61d8a000d9ae017 Mon Sep 17 00:00:00 2001 From: a Date: Sat, 24 Dec 2022 15:02:47 -0600 Subject: [PATCH 2/4] missed a few --- btreeg.go | 29 +---------------------------- btreeg_impl.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/btreeg.go b/btreeg.go index fa65073..c8e1b5a 100644 --- a/btreeg.go +++ b/btreeg.go @@ -90,16 +90,6 @@ func (tr *BTreeG[T]) ScanMut(iter func(item T) bool) { tr.scan(iter, true) } -func (tr *BTreeG[T]) scan(iter func(item T) bool, mut bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return - } - tr.nodeScan(&tr.root, iter, mut) -} - // Get a value for key func (tr *BTreeG[T]) Get(key T) (T, bool) { return tr.getHint(key, nil, false) @@ -154,15 +144,6 @@ func (tr *BTreeG[T]) Reverse(iter func(item T) bool) { func (tr *BTreeG[T]) ReverseMut(iter func(item T) bool) { tr.reverse(iter, true) } -func (tr *BTreeG[T]) reverse(iter func(item T) bool, mut bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return - } - tr.nodeReverse(&tr.root, iter, mut) -} // Descend the tree within the range [pivot, first] // Pass nil for pivot to scan all item in descending order @@ -173,15 +154,6 @@ func (tr *BTreeG[T]) Descend(pivot T, iter func(item T) bool) { func (tr *BTreeG[T]) DescendMut(pivot T, iter func(item T) bool) { tr.descend(pivot, iter, true) } -func (tr *BTreeG[T]) descend(pivot T, iter func(item T) bool, mut bool) { - if tr.lock(mut) { - defer tr.unlock(mut) - } - if tr.root == nil { - return - } - tr.nodeDescend(&tr.root, pivot, nil, 0, iter, mut) -} // Load is for bulk loading pre-sorted items func (tr *BTreeG[T]) Load(item T) (T, bool) { @@ -415,6 +387,7 @@ func (tr *BTreeG[T]) Height() int { func (tr *BTreeG[T]) Walk(iter func(item []T) bool) { tr.walk(iter, false) } + func (tr *BTreeG[T]) WalkMut(iter func(item []T) bool) { tr.walk(iter, true) } diff --git a/btreeg_impl.go b/btreeg_impl.go index ee36a98..bebecd5 100644 --- a/btreeg_impl.go +++ b/btreeg_impl.go @@ -263,6 +263,16 @@ func (tr *BTreeG[T]) nodeSet(cn **node[T], item T, return prev, replaced, false } +func (tr *BTreeG[T]) scan(iter func(item T) bool, mut bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return + } + tr.nodeScan(&tr.root, iter, mut) +} + func (tr *BTreeG[T]) nodeScan(cn **node[T], iter func(item T) bool, mut bool, ) bool { n := tr.isoLoad(cn, mut) @@ -500,6 +510,16 @@ func (tr *BTreeG[T]) nodeAscend(cn **node[T], pivot T, hint *PathHint, return true } +func (tr *BTreeG[T]) reverse(iter func(item T) bool, mut bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return + } + tr.nodeReverse(&tr.root, iter, mut) +} + func (tr *BTreeG[T]) nodeReverse(cn **node[T], iter func(item T) bool, mut bool, ) bool { n := tr.isoLoad(cn, mut) @@ -525,6 +545,16 @@ func (tr *BTreeG[T]) nodeReverse(cn **node[T], iter func(item T) bool, mut bool, return true } +func (tr *BTreeG[T]) descend(pivot T, iter func(item T) bool, mut bool) { + if tr.lock(mut) { + defer tr.unlock(mut) + } + if tr.root == nil { + return + } + tr.nodeDescend(&tr.root, pivot, nil, 0, iter, mut) +} + func (tr *BTreeG[T]) nodeDescend(cn **node[T], pivot T, hint *PathHint, depth int, iter func(item T) bool, mut bool, ) bool { From fd3315040f0c58d78e7950014ace0ce2802b4fd0 Mon Sep 17 00:00:00 2001 From: a Date: Sat, 24 Dec 2022 15:05:05 -0600 Subject: [PATCH 3/4] reorder --- btreeg_deprecated.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/btreeg_deprecated.go b/btreeg_deprecated.go index 72cfd59..2e562a7 100644 --- a/btreeg_deprecated.go +++ b/btreeg_deprecated.go @@ -7,6 +7,10 @@ type Generic[T any] struct { *BTreeG[T] } +func (tr *Generic[T]) Copy() *Generic[T] { + return &Generic[T]{tr.BTreeG.Copy()} +} + // NewGeneric returns a generic BTree // // Deprecated: use NewBTreeG @@ -21,7 +25,3 @@ func NewGenericOptions[T any](less func(a, b T) bool, opts Options, ) *Generic[T] { return &Generic[T]{NewBTreeGOptions(less, opts)} } - -func (tr *Generic[T]) Copy() *Generic[T] { - return &Generic[T]{tr.BTreeG.Copy()} -} From 0e0063bd5c79e7a369d674edaa9bed5b28feaed0 Mon Sep 17 00:00:00 2001 From: a Date: Sat, 24 Dec 2022 15:12:17 -0600 Subject: [PATCH 4/4] do map --- map.go | 797 ---------------------------------------------------- map_impl.go | 599 +++++++++++++++++++++++++++++++++++++++ map_iter.go | 202 +++++++++++++ 3 files changed, 801 insertions(+), 797 deletions(-) create mode 100644 map_impl.go create mode 100644 map_iter.go diff --git a/map.go b/map.go index a4d7c04..be5ce1f 100644 --- a/map.go +++ b/map.go @@ -3,47 +3,6 @@ // license that can be found in the LICENSE file. package btree -import "sync/atomic" - -type ordered interface { - ~int | ~int8 | ~int16 | ~int32 | ~int64 | - ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | - ~float32 | ~float64 | ~string -} - -type copier[T any] interface { - Copy() T -} - -type isoCopier[T any] interface { - IsoCopy() T -} - -func degreeToMinMax(deg int) (min, max int) { - if deg <= 0 { - deg = 32 - } else if deg == 1 { - deg = 2 // must have at least 2 - } - max = deg*2 - 1 // max items per node. max children is +1 - min = max / 2 - return min, max -} - -var gisoid uint64 - -func newIsoID() uint64 { - return atomic.AddUint64(&gisoid, 1) -} - -type mapPair[K ordered, V any] struct { - // The `value` field should be before the `key` field because doing so - // allows for the Go compiler to optimize away the `value` field when - // it's a `struct{}`, which is the case for `btree.Set`. - value V - key K -} - type Map[K ordered, V any] struct { isoid uint64 root *mapNode[K, V] @@ -68,40 +27,6 @@ type mapNode[K ordered, V any] struct { children *[]*mapNode[K, V] } -// Copy the node for safe isolation. -func (tr *Map[K, V]) copy(n *mapNode[K, V]) *mapNode[K, V] { - n2 := new(mapNode[K, V]) - n2.isoid = tr.isoid - n2.count = n.count - n2.items = make([]mapPair[K, V], len(n.items), cap(n.items)) - copy(n2.items, n.items) - if tr.copyValues { - for i := 0; i < len(n2.items); i++ { - n2.items[i].value = - ((interface{})(n2.items[i].value)).(copier[V]).Copy() - } - } else if tr.isoCopyValues { - for i := 0; i < len(n2.items); i++ { - n2.items[i].value = - ((interface{})(n2.items[i].value)).(isoCopier[V]).IsoCopy() - } - } - if !n.leaf() { - n2.children = new([]*mapNode[K, V]) - *n2.children = make([]*mapNode[K, V], len(*n.children), tr.max+1) - copy(*n2.children, *n.children) - } - return n2 -} - -// isoLoad loads the provided node and, if needed, performs a copy-on-write. -func (tr *Map[K, V]) isoLoad(cn **mapNode[K, V], mut bool) *mapNode[K, V] { - if mut && (*cn).isoid != tr.isoid { - *cn = tr.copy(*cn) - } - return *cn -} - func (tr *Map[K, V]) Copy() *Map[K, V] { return tr.IsoCopy() } @@ -114,47 +39,6 @@ func (tr *Map[K, V]) IsoCopy() *Map[K, V] { return tr2 } -func (tr *Map[K, V]) newNode(leaf bool) *mapNode[K, V] { - n := new(mapNode[K, V]) - n.isoid = tr.isoid - if !leaf { - n.children = new([]*mapNode[K, V]) - } - return n -} - -// leaf returns true if the node is a leaf. -func (n *mapNode[K, V]) leaf() bool { - return n.children == nil -} - -func (tr *Map[K, V]) search(n *mapNode[K, V], key K) (index int, found bool) { - low, high := 0, len(n.items) - for low < high { - h := (low + high) / 2 - if !(key < n.items[h].key) { - low = h + 1 - } else { - high = h - } - } - if low > 0 && !(n.items[low-1].key < key) { - return low - 1, true - } - return low, false -} - -func (tr *Map[K, V]) init(degree int) { - if tr.min != 0 { - return - } - tr.min, tr.max = degreeToMinMax(degree) - _, tr.copyValues = ((interface{})(tr.empty.value)).(copier[V]) - if !tr.copyValues { - _, tr.isoCopyValues = ((interface{})(tr.empty.value)).(isoCopier[V]) - } -} - // Set or replace a value for a key func (tr *Map[K, V]) Set(key K, value V) (V, bool) { item := mapPair[K, V]{key: key, value: value} @@ -184,77 +68,6 @@ func (tr *Map[K, V]) Set(key K, value V) (V, bool) { return tr.empty.value, false } -func (tr *Map[K, V]) nodeSplit(n *mapNode[K, V], -) (right *mapNode[K, V], median mapPair[K, V]) { - i := tr.max / 2 - median = n.items[i] - - // right node - right = tr.newNode(n.leaf()) - right.items = n.items[i+1:] - if !n.leaf() { - *right.children = (*n.children)[i+1:] - } - right.updateCount() - - // left node - n.items[i] = tr.empty - n.items = n.items[:i:i] - if !n.leaf() { - *n.children = (*n.children)[: i+1 : i+1] - } - n.updateCount() - return right, median -} - -func (n *mapNode[K, V]) updateCount() { - n.count = len(n.items) - if !n.leaf() { - for i := 0; i < len(*n.children); i++ { - n.count += (*n.children)[i].count - } - } -} - -func (tr *Map[K, V]) nodeSet(pn **mapNode[K, V], item mapPair[K, V], -) (prev V, replaced bool, split bool) { - n := tr.isoLoad(pn, true) - i, found := tr.search(n, item.key) - if found { - prev = n.items[i].value - n.items[i] = item - return prev, true, false - } - if n.leaf() { - if len(n.items) == tr.max { - return tr.empty.value, false, true - } - n.items = append(n.items, tr.empty) - copy(n.items[i+1:], n.items[i:]) - n.items[i] = item - n.count++ - return tr.empty.value, false, false - } - prev, replaced, split = tr.nodeSet(&(*n.children)[i], item) - if split { - if len(n.items) == tr.max { - return tr.empty.value, false, true - } - right, median := tr.nodeSplit((*n.children)[i]) - *n.children = append(*n.children, nil) - copy((*n.children)[i+1:], (*n.children)[i:]) - (*n.children)[i+1] = right - n.items = append(n.items, tr.empty) - copy(n.items[i+1:], n.items[i:]) - n.items[i] = median - return tr.nodeSet(&n, item) - } - if !replaced { - n.count++ - } - return prev, replaced, false -} - func (tr *Map[K, V]) Scan(iter func(key K, value V) bool) { tr.scan(iter, false) } @@ -263,36 +76,6 @@ func (tr *Map[K, V]) ScanMut(iter func(key K, value V) bool) { tr.scan(iter, true) } -func (tr *Map[K, V]) scan(iter func(key K, value V) bool, mut bool) { - if tr.root == nil { - return - } - tr.nodeScan(&tr.root, iter, mut) -} - -func (tr *Map[K, V]) nodeScan(cn **mapNode[K, V], - iter func(key K, value V) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - if n.leaf() { - for i := 0; i < len(n.items); i++ { - if !iter(n.items[i].key, n.items[i].value) { - return false - } - } - return true - } - for i := 0; i < len(n.items); i++ { - if !tr.nodeScan(&(*n.children)[i], iter, mut) { - return false - } - if !iter(n.items[i].key, n.items[i].value) { - return false - } - } - return tr.nodeScan(&(*n.children)[len(*n.children)-1], iter, mut) -} - // Get a value for key. func (tr *Map[K, V]) Get(key K) (V, bool) { return tr.get(key, false) @@ -313,23 +96,6 @@ func (tr *Map[K, V]) GetMut(key K) (V, bool) { return tr.get(key, true) } -func (tr *Map[K, V]) get(key K, mut bool) (V, bool) { - if tr.root == nil { - return tr.empty.value, false - } - n := tr.isoLoad(&tr.root, mut) - for { - i, found := tr.search(n, key) - if found { - return n.items[i].value, true - } - if n.leaf() { - return tr.empty.value, false - } - n = tr.isoLoad(&(*n.children)[i], mut) - } -} - // Len returns the number of items in the tree func (tr *Map[K, V]) Len() int { return tr.count @@ -355,137 +121,6 @@ func (tr *Map[K, V]) Delete(key K) (V, bool) { return prev.value, true } -func (tr *Map[K, V]) delete(pn **mapNode[K, V], max bool, key K, -) (mapPair[K, V], bool) { - n := tr.isoLoad(pn, true) - var i int - var found bool - if max { - i, found = len(n.items)-1, true - } else { - i, found = tr.search(n, key) - } - if n.leaf() { - if found { - // found the items at the leaf, remove it and return. - prev := n.items[i] - copy(n.items[i:], n.items[i+1:]) - n.items[len(n.items)-1] = tr.empty - n.items = n.items[:len(n.items)-1] - n.count-- - return prev, true - } - return tr.empty, false - } - - var prev mapPair[K, V] - var deleted bool - if found { - if max { - i++ - prev, deleted = tr.delete(&(*n.children)[i], true, tr.empty.key) - } else { - prev = n.items[i] - maxItem, _ := tr.delete(&(*n.children)[i], true, tr.empty.key) - deleted = true - n.items[i] = maxItem - } - } else { - prev, deleted = tr.delete(&(*n.children)[i], max, key) - } - if !deleted { - return tr.empty, false - } - n.count-- - if len((*n.children)[i].items) < tr.min { - tr.nodeRebalance(n, i) - } - return prev, true -} - -// nodeRebalance rebalances the child nodes following a delete operation. -// Provide the index of the child node with the number of items that fell -// below minItems. -func (tr *Map[K, V]) nodeRebalance(n *mapNode[K, V], i int) { - if i == len(n.items) { - i-- - } - - // ensure copy-on-write - left := tr.isoLoad(&(*n.children)[i], true) - right := tr.isoLoad(&(*n.children)[i+1], true) - - if len(left.items)+len(right.items) < tr.max { - // Merges the left and right children nodes together as a single node - // that includes (left,item,right), and places the contents into the - // existing left node. Delete the right node altogether and move the - // following items and child nodes to the left by one slot. - - // merge (left,item,right) - left.items = append(left.items, n.items[i]) - left.items = append(left.items, right.items...) - if !left.leaf() { - *left.children = append(*left.children, *right.children...) - } - left.count += right.count + 1 - - // move the items over one slot - copy(n.items[i:], n.items[i+1:]) - n.items[len(n.items)-1] = tr.empty - n.items = n.items[:len(n.items)-1] - - // move the children over one slot - copy((*n.children)[i+1:], (*n.children)[i+2:]) - (*n.children)[len(*n.children)-1] = nil - (*n.children) = (*n.children)[:len(*n.children)-1] - } else if len(left.items) > len(right.items) { - // move left -> right over one slot - - // Move the item of the parent node at index into the right-node first - // slot, and move the left-node last item into the previously moved - // parent item slot. - right.items = append(right.items, tr.empty) - copy(right.items[1:], right.items) - right.items[0] = n.items[i] - right.count++ - n.items[i] = left.items[len(left.items)-1] - left.items[len(left.items)-1] = tr.empty - left.items = left.items[:len(left.items)-1] - left.count-- - - if !left.leaf() { - // move the left-node last child into the right-node first slot - *right.children = append(*right.children, nil) - copy((*right.children)[1:], *right.children) - (*right.children)[0] = (*left.children)[len(*left.children)-1] - (*left.children)[len(*left.children)-1] = nil - (*left.children) = (*left.children)[:len(*left.children)-1] - left.count -= (*right.children)[0].count - right.count += (*right.children)[0].count - } - } else { - // move left <- right over one slot - - // Same as above but the other direction - left.items = append(left.items, n.items[i]) - left.count++ - n.items[i] = right.items[0] - copy(right.items, right.items[1:]) - right.items[len(right.items)-1] = tr.empty - right.items = right.items[:len(right.items)-1] - right.count-- - - if !left.leaf() { - *left.children = append(*left.children, (*right.children)[0]) - copy(*right.children, (*right.children)[1:]) - (*right.children)[len(*right.children)-1] = nil - *right.children = (*right.children)[:len(*right.children)-1] - left.count += (*left.children)[len(*left.children)-1].count - right.count -= (*left.children)[len(*left.children)-1].count - } - } -} - // Ascend the tree within the range [pivot, last] // Pass nil for pivot to scan all item in ascending order // Return false to stop iterating @@ -497,44 +132,6 @@ func (tr *Map[K, V]) AscendMut(pivot K, iter func(key K, value V) bool) { tr.ascend(pivot, iter, true) } -func (tr *Map[K, V]) ascend(pivot K, iter func(key K, value V) bool, mut bool) { - if tr.root == nil { - return - } - tr.nodeAscend(&tr.root, pivot, iter, mut) -} - -// The return value of this function determines whether we should keep iterating -// upon this functions return. -func (tr *Map[K, V]) nodeAscend(cn **mapNode[K, V], pivot K, - iter func(key K, value V) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - i, found := tr.search(n, pivot) - if !found { - if !n.leaf() { - if !tr.nodeAscend(&(*n.children)[i], pivot, iter, mut) { - return false - } - } - } - // We are either in the case that - // - node is found, we should iterate through it starting at `i`, - // the index it was located at. - // - node is not found, and TODO: fill in. - for ; i < len(n.items); i++ { - if !iter(n.items[i].key, n.items[i].value) { - return false - } - if !n.leaf() { - if !tr.nodeScan(&(*n.children)[i+1], iter, mut) { - return false - } - } - } - return true -} - func (tr *Map[K, V]) Reverse(iter func(key K, value V) bool) { tr.reverse(iter, false) } @@ -543,39 +140,6 @@ func (tr *Map[K, V]) ReverseMut(iter func(key K, value V) bool) { tr.reverse(iter, true) } -func (tr *Map[K, V]) reverse(iter func(key K, value V) bool, mut bool) { - if tr.root == nil { - return - } - tr.nodeReverse(&tr.root, iter, mut) -} - -func (tr *Map[K, V]) nodeReverse(cn **mapNode[K, V], - iter func(key K, value V) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - if n.leaf() { - for i := len(n.items) - 1; i >= 0; i-- { - if !iter(n.items[i].key, n.items[i].value) { - return false - } - } - return true - } - if !tr.nodeReverse(&(*n.children)[len(*n.children)-1], iter, mut) { - return false - } - for i := len(n.items) - 1; i >= 0; i-- { - if !iter(n.items[i].key, n.items[i].value) { - return false - } - if !tr.nodeReverse(&(*n.children)[i], iter, mut) { - return false - } - } - return true -} - // Descend the tree within the range [pivot, first] // Pass nil for pivot to scan all item in descending order // Return false to stop iterating @@ -587,43 +151,6 @@ func (tr *Map[K, V]) DescendMut(pivot K, iter func(key K, value V) bool) { tr.descend(pivot, iter, true) } -func (tr *Map[K, V]) descend( - pivot K, - iter func(key K, value V) bool, - mut bool, -) { - if tr.root == nil { - return - } - tr.nodeDescend(&tr.root, pivot, iter, mut) -} - -func (tr *Map[K, V]) nodeDescend(cn **mapNode[K, V], pivot K, - iter func(key K, value V) bool, mut bool, -) bool { - n := tr.isoLoad(cn, mut) - i, found := tr.search(n, pivot) - if !found { - if !n.leaf() { - if !tr.nodeDescend(&(*n.children)[i], pivot, iter, mut) { - return false - } - } - i-- - } - for ; i >= 0; i-- { - if !iter(n.items[i].key, n.items[i].value) { - return false - } - if !n.leaf() { - if !tr.nodeReverse(&(*n.children)[i], iter, mut) { - return false - } - } - } - return true -} - // Load is for bulk loading pre-sorted items func (tr *Map[K, V]) Load(key K, value V) (V, bool) { item := mapPair[K, V]{key: key, value: value} @@ -667,20 +194,6 @@ func (tr *Map[K, V]) MinMut() (K, V, bool) { return tr.minMut(true) } -func (tr *Map[K, V]) minMut(mut bool) (key K, value V, ok bool) { - if tr.root == nil { - return key, value, false - } - n := tr.isoLoad(&tr.root, mut) - for { - if n.leaf() { - item := n.items[0] - return item.key, item.value, true - } - n = tr.isoLoad(&(*n.children)[0], mut) - } -} - // Max returns the maximum item in tree. // Returns nil if the tree has no items. func (tr *Map[K, V]) Max() (K, V, bool) { @@ -691,20 +204,6 @@ func (tr *Map[K, V]) MaxMut() (K, V, bool) { return tr.maxMut(true) } -func (tr *Map[K, V]) maxMut(mut bool) (K, V, bool) { - if tr.root == nil { - return tr.empty.key, tr.empty.value, false - } - n := tr.isoLoad(&tr.root, mut) - for { - if n.leaf() { - item := n.items[len(n.items)-1] - return item.key, item.value, true - } - n = tr.isoLoad(&(*n.children)[len(*n.children)-1], mut) - } -} - // PopMin removes the minimum item in tree and returns it. // Returns nil if the tree has no items. func (tr *Map[K, V]) PopMin() (K, V, bool) { @@ -798,28 +297,6 @@ func (tr *Map[K, V]) GetAtMut(index int) (K, V, bool) { return tr.getAt(index, true) } -func (tr *Map[K, V]) getAt(index int, mut bool) (K, V, bool) { - if tr.root == nil || index < 0 || index >= tr.count { - return tr.empty.key, tr.empty.value, false - } - n := tr.isoLoad(&tr.root, mut) - for { - if n.leaf() { - return n.items[index].key, n.items[index].value, true - } - i := 0 - for ; i < len(n.items); i++ { - if index < (*n.children)[i].count { - break - } else if index == (*n.children)[i].count { - return n.items[i].key, n.items[i].value, true - } - index -= (*n.children)[i].count + 1 - } - n = tr.isoLoad(&(*n.children)[i], mut) - } -} - // DeleteAt deletes the item at index. // Return nil if the tree is empty or the index is out of bounds. func (tr *Map[K, V]) DeleteAt(index int) (K, V, bool) { @@ -895,22 +372,6 @@ func (tr *Map[K, V]) Height() int { return height } -// MapIter represents an iterator for btree.Map -type MapIter[K ordered, V any] struct { - tr *Map[K, V] - mut bool - seeked bool - atstart bool - atend bool - stack []mapIterStackItem[K, V] - item mapPair[K, V] -} - -type mapIterStackItem[K ordered, V any] struct { - n *mapNode[K, V] - i int -} - // Iter returns a read-only iterator. func (tr *Map[K, V]) Iter() MapIter[K, V] { return tr.iter(false) @@ -920,198 +381,6 @@ func (tr *Map[K, V]) IterMut() MapIter[K, V] { return tr.iter(true) } -func (tr *Map[K, V]) iter(mut bool) MapIter[K, V] { - var iter MapIter[K, V] - iter.tr = tr - iter.mut = mut - return iter -} - -// Seek to item greater-or-equal-to key. -// Returns false if there was no item found. -func (iter *MapIter[K, V]) Seek(key K) bool { - if iter.tr == nil { - return false - } - iter.seeked = true - iter.stack = iter.stack[:0] - if iter.tr.root == nil { - return false - } - n := iter.tr.isoLoad(&iter.tr.root, iter.mut) - for { - i, found := iter.tr.search(n, key) - iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, i}) - if found { - iter.item = n.items[i] - return true - } - if n.leaf() { - iter.stack[len(iter.stack)-1].i-- - return iter.Next() - } - n = iter.tr.isoLoad(&(*n.children)[i], iter.mut) - } -} - -// First moves iterator to first item in tree. -// Returns false if the tree is empty. -func (iter *MapIter[K, V]) First() bool { - if iter.tr == nil { - return false - } - iter.atend = false - iter.atstart = false - iter.seeked = true - iter.stack = iter.stack[:0] - if iter.tr.root == nil { - return false - } - n := iter.tr.isoLoad(&iter.tr.root, iter.mut) - for { - iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, 0}) - if n.leaf() { - break - } - n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) - } - s := &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Last moves iterator to last item in tree. -// Returns false if the tree is empty. -func (iter *MapIter[K, V]) Last() bool { - if iter.tr == nil { - return false - } - iter.seeked = true - iter.stack = iter.stack[:0] - if iter.tr.root == nil { - return false - } - n := iter.tr.isoLoad(&iter.tr.root, iter.mut) - for { - iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, len(n.items)}) - if n.leaf() { - iter.stack[len(iter.stack)-1].i-- - break - } - n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) - } - s := &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Next moves iterator to the next item in iterator. -// Returns false if the tree is empty or the iterator is at the end of -// the tree. -func (iter *MapIter[K, V]) Next() bool { - if iter.tr == nil { - return false - } - if !iter.seeked { - return iter.First() - } - if len(iter.stack) == 0 { - if iter.atstart { - return iter.First() && iter.Next() - } - return false - } - s := &iter.stack[len(iter.stack)-1] - s.i++ - if s.n.leaf() { - if s.i == len(s.n.items) { - for { - iter.stack = iter.stack[:len(iter.stack)-1] - if len(iter.stack) == 0 { - iter.atend = true - return false - } - s = &iter.stack[len(iter.stack)-1] - if s.i < len(s.n.items) { - break - } - } - } - } else { - n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) - for { - iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, 0}) - if n.leaf() { - break - } - n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) - } - } - s = &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Prev moves iterator to the previous item in iterator. -// Returns false if the tree is empty or the iterator is at the beginning of -// the tree. -func (iter *MapIter[K, V]) Prev() bool { - if iter.tr == nil { - return false - } - if !iter.seeked { - return false - } - if len(iter.stack) == 0 { - if iter.atend { - return iter.Last() && iter.Prev() - } - return false - } - s := &iter.stack[len(iter.stack)-1] - if s.n.leaf() { - s.i-- - if s.i == -1 { - for { - iter.stack = iter.stack[:len(iter.stack)-1] - if len(iter.stack) == 0 { - iter.atstart = true - return false - } - s = &iter.stack[len(iter.stack)-1] - s.i-- - if s.i > -1 { - break - } - } - } - } else { - n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) - for { - iter.stack = append(iter.stack, - mapIterStackItem[K, V]{n, len(n.items)}) - if n.leaf() { - iter.stack[len(iter.stack)-1].i-- - break - } - n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) - } - } - s = &iter.stack[len(iter.stack)-1] - iter.item = s.n.items[s.i] - return true -} - -// Key returns the current iterator item key. -func (iter *MapIter[K, V]) Key() K { - return iter.item.key -} - -// Value returns the current iterator item value. -func (iter *MapIter[K, V]) Value() V { - return iter.item.value -} - // Values returns all the values in order. func (tr *Map[K, V]) Values() []V { return tr.values(false) @@ -1121,29 +390,6 @@ func (tr *Map[K, V]) ValuesMut() []V { return tr.values(true) } -func (tr *Map[K, V]) values(mut bool) []V { - values := make([]V, 0, tr.Len()) - if tr.root != nil { - values = tr.nodeValues(&tr.root, values, mut) - } - return values -} - -func (tr *Map[K, V]) nodeValues(cn **mapNode[K, V], values []V, mut bool) []V { - n := tr.isoLoad(cn, mut) - if n.leaf() { - for i := 0; i < len(n.items); i++ { - values = append(values, n.items[i].value) - } - return values - } - for i := 0; i < len(n.items); i++ { - values = tr.nodeValues(&(*n.children)[i], values, mut) - values = append(values, n.items[i].value) - } - return tr.nodeValues(&(*n.children)[len(*n.children)-1], values, mut) -} - // Keys returns all the keys in order. func (tr *Map[K, V]) Keys() []K { keys := make([]K, 0, tr.Len()) @@ -1153,20 +399,6 @@ func (tr *Map[K, V]) Keys() []K { return keys } -func (n *mapNode[K, V]) keys(keys []K) []K { - if n.leaf() { - for i := 0; i < len(n.items); i++ { - keys = append(keys, n.items[i].key) - } - return keys - } - for i := 0; i < len(n.items); i++ { - keys = (*n.children)[i].keys(keys) - keys = append(keys, n.items[i].key) - } - return (*n.children)[len(*n.children)-1].keys(keys) -} - // KeyValues returns all the keys and values in order. func (tr *Map[K, V]) KeyValues() ([]K, []V) { return tr.keyValues(false) @@ -1176,35 +408,6 @@ func (tr *Map[K, V]) KeyValuesMut() ([]K, []V) { return tr.keyValues(true) } -func (tr *Map[K, V]) keyValues(mut bool) ([]K, []V) { - keys := make([]K, 0, tr.Len()) - values := make([]V, 0, tr.Len()) - if tr.root != nil { - keys, values = tr.nodeKeyValues(&tr.root, keys, values, mut) - } - return keys, values -} - -func (tr *Map[K, V]) nodeKeyValues(cn **mapNode[K, V], keys []K, values []V, - mut bool, -) ([]K, []V) { - n := tr.isoLoad(cn, mut) - if n.leaf() { - for i := 0; i < len(n.items); i++ { - keys = append(keys, n.items[i].key) - values = append(values, n.items[i].value) - } - return keys, values - } - for i := 0; i < len(n.items); i++ { - keys, values = tr.nodeKeyValues(&(*n.children)[i], keys, values, mut) - keys = append(keys, n.items[i].key) - values = append(values, n.items[i].value) - } - return tr.nodeKeyValues(&(*n.children)[len(*n.children)-1], keys, values, - mut) -} - // Clear will delete all items. func (tr *Map[K, V]) Clear() { tr.count = 0 diff --git a/map_impl.go b/map_impl.go new file mode 100644 index 0000000..b37734b --- /dev/null +++ b/map_impl.go @@ -0,0 +1,599 @@ +package btree + +import "sync/atomic" + +type ordered interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | ~string +} + +type copier[T any] interface { + Copy() T +} + +type isoCopier[T any] interface { + IsoCopy() T +} + +func degreeToMinMax(deg int) (min, max int) { + if deg <= 0 { + deg = 32 + } else if deg == 1 { + deg = 2 // must have at least 2 + } + max = deg*2 - 1 // max items per node. max children is +1 + min = max / 2 + return min, max +} + +var gisoid uint64 + +func newIsoID() uint64 { + return atomic.AddUint64(&gisoid, 1) +} + +type mapPair[K ordered, V any] struct { + // The `value` field should be before the `key` field because doing so + // allows for the Go compiler to optimize away the `value` field when + // it's a `struct{}`, which is the case for `btree.Set`. + value V + key K +} + +// Copy the node for safe isolation. +func (tr *Map[K, V]) copy(n *mapNode[K, V]) *mapNode[K, V] { + n2 := new(mapNode[K, V]) + n2.isoid = tr.isoid + n2.count = n.count + n2.items = make([]mapPair[K, V], len(n.items), cap(n.items)) + copy(n2.items, n.items) + if tr.copyValues { + for i := 0; i < len(n2.items); i++ { + n2.items[i].value = + ((interface{})(n2.items[i].value)).(copier[V]).Copy() + } + } else if tr.isoCopyValues { + for i := 0; i < len(n2.items); i++ { + n2.items[i].value = + ((interface{})(n2.items[i].value)).(isoCopier[V]).IsoCopy() + } + } + if !n.leaf() { + n2.children = new([]*mapNode[K, V]) + *n2.children = make([]*mapNode[K, V], len(*n.children), tr.max+1) + copy(*n2.children, *n.children) + } + return n2 +} + +// isoLoad loads the provided node and, if needed, performs a copy-on-write. +func (tr *Map[K, V]) isoLoad(cn **mapNode[K, V], mut bool) *mapNode[K, V] { + if mut && (*cn).isoid != tr.isoid { + *cn = tr.copy(*cn) + } + return *cn +} + +func (tr *Map[K, V]) newNode(leaf bool) *mapNode[K, V] { + n := new(mapNode[K, V]) + n.isoid = tr.isoid + if !leaf { + n.children = new([]*mapNode[K, V]) + } + return n +} + +// leaf returns true if the node is a leaf. +func (n *mapNode[K, V]) leaf() bool { + return n.children == nil +} + +func (tr *Map[K, V]) search(n *mapNode[K, V], key K) (index int, found bool) { + low, high := 0, len(n.items) + for low < high { + h := (low + high) / 2 + if !(key < n.items[h].key) { + low = h + 1 + } else { + high = h + } + } + if low > 0 && !(n.items[low-1].key < key) { + return low - 1, true + } + return low, false +} + +func (tr *Map[K, V]) init(degree int) { + if tr.min != 0 { + return + } + tr.min, tr.max = degreeToMinMax(degree) + _, tr.copyValues = ((interface{})(tr.empty.value)).(copier[V]) + if !tr.copyValues { + _, tr.isoCopyValues = ((interface{})(tr.empty.value)).(isoCopier[V]) + } +} + +func (tr *Map[K, V]) nodeSplit(n *mapNode[K, V], +) (right *mapNode[K, V], median mapPair[K, V]) { + i := tr.max / 2 + median = n.items[i] + + // right node + right = tr.newNode(n.leaf()) + right.items = n.items[i+1:] + if !n.leaf() { + *right.children = (*n.children)[i+1:] + } + right.updateCount() + + // left node + n.items[i] = tr.empty + n.items = n.items[:i:i] + if !n.leaf() { + *n.children = (*n.children)[: i+1 : i+1] + } + n.updateCount() + return right, median +} + +func (n *mapNode[K, V]) updateCount() { + n.count = len(n.items) + if !n.leaf() { + for i := 0; i < len(*n.children); i++ { + n.count += (*n.children)[i].count + } + } +} + +func (tr *Map[K, V]) nodeSet(pn **mapNode[K, V], item mapPair[K, V], +) (prev V, replaced bool, split bool) { + n := tr.isoLoad(pn, true) + i, found := tr.search(n, item.key) + if found { + prev = n.items[i].value + n.items[i] = item + return prev, true, false + } + if n.leaf() { + if len(n.items) == tr.max { + return tr.empty.value, false, true + } + n.items = append(n.items, tr.empty) + copy(n.items[i+1:], n.items[i:]) + n.items[i] = item + n.count++ + return tr.empty.value, false, false + } + prev, replaced, split = tr.nodeSet(&(*n.children)[i], item) + if split { + if len(n.items) == tr.max { + return tr.empty.value, false, true + } + right, median := tr.nodeSplit((*n.children)[i]) + *n.children = append(*n.children, nil) + copy((*n.children)[i+1:], (*n.children)[i:]) + (*n.children)[i+1] = right + n.items = append(n.items, tr.empty) + copy(n.items[i+1:], n.items[i:]) + n.items[i] = median + return tr.nodeSet(&n, item) + } + if !replaced { + n.count++ + } + return prev, replaced, false +} + +func (tr *Map[K, V]) scan(iter func(key K, value V) bool, mut bool) { + if tr.root == nil { + return + } + tr.nodeScan(&tr.root, iter, mut) +} + +func (tr *Map[K, V]) nodeScan(cn **mapNode[K, V], + iter func(key K, value V) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + if n.leaf() { + for i := 0; i < len(n.items); i++ { + if !iter(n.items[i].key, n.items[i].value) { + return false + } + } + return true + } + for i := 0; i < len(n.items); i++ { + if !tr.nodeScan(&(*n.children)[i], iter, mut) { + return false + } + if !iter(n.items[i].key, n.items[i].value) { + return false + } + } + return tr.nodeScan(&(*n.children)[len(*n.children)-1], iter, mut) +} + +func (tr *Map[K, V]) get(key K, mut bool) (V, bool) { + if tr.root == nil { + return tr.empty.value, false + } + n := tr.isoLoad(&tr.root, mut) + for { + i, found := tr.search(n, key) + if found { + return n.items[i].value, true + } + if n.leaf() { + return tr.empty.value, false + } + n = tr.isoLoad(&(*n.children)[i], mut) + } +} + +func (tr *Map[K, V]) delete(pn **mapNode[K, V], max bool, key K, +) (mapPair[K, V], bool) { + n := tr.isoLoad(pn, true) + var i int + var found bool + if max { + i, found = len(n.items)-1, true + } else { + i, found = tr.search(n, key) + } + if n.leaf() { + if found { + // found the items at the leaf, remove it and return. + prev := n.items[i] + copy(n.items[i:], n.items[i+1:]) + n.items[len(n.items)-1] = tr.empty + n.items = n.items[:len(n.items)-1] + n.count-- + return prev, true + } + return tr.empty, false + } + + var prev mapPair[K, V] + var deleted bool + if found { + if max { + i++ + prev, deleted = tr.delete(&(*n.children)[i], true, tr.empty.key) + } else { + prev = n.items[i] + maxItem, _ := tr.delete(&(*n.children)[i], true, tr.empty.key) + deleted = true + n.items[i] = maxItem + } + } else { + prev, deleted = tr.delete(&(*n.children)[i], max, key) + } + if !deleted { + return tr.empty, false + } + n.count-- + if len((*n.children)[i].items) < tr.min { + tr.nodeRebalance(n, i) + } + return prev, true +} + +// nodeRebalance rebalances the child nodes following a delete operation. +// Provide the index of the child node with the number of items that fell +// below minItems. +func (tr *Map[K, V]) nodeRebalance(n *mapNode[K, V], i int) { + if i == len(n.items) { + i-- + } + + // ensure copy-on-write + left := tr.isoLoad(&(*n.children)[i], true) + right := tr.isoLoad(&(*n.children)[i+1], true) + + if len(left.items)+len(right.items) < tr.max { + // Merges the left and right children nodes together as a single node + // that includes (left,item,right), and places the contents into the + // existing left node. Delete the right node altogether and move the + // following items and child nodes to the left by one slot. + + // merge (left,item,right) + left.items = append(left.items, n.items[i]) + left.items = append(left.items, right.items...) + if !left.leaf() { + *left.children = append(*left.children, *right.children...) + } + left.count += right.count + 1 + + // move the items over one slot + copy(n.items[i:], n.items[i+1:]) + n.items[len(n.items)-1] = tr.empty + n.items = n.items[:len(n.items)-1] + + // move the children over one slot + copy((*n.children)[i+1:], (*n.children)[i+2:]) + (*n.children)[len(*n.children)-1] = nil + (*n.children) = (*n.children)[:len(*n.children)-1] + } else if len(left.items) > len(right.items) { + // move left -> right over one slot + + // Move the item of the parent node at index into the right-node first + // slot, and move the left-node last item into the previously moved + // parent item slot. + right.items = append(right.items, tr.empty) + copy(right.items[1:], right.items) + right.items[0] = n.items[i] + right.count++ + n.items[i] = left.items[len(left.items)-1] + left.items[len(left.items)-1] = tr.empty + left.items = left.items[:len(left.items)-1] + left.count-- + + if !left.leaf() { + // move the left-node last child into the right-node first slot + *right.children = append(*right.children, nil) + copy((*right.children)[1:], *right.children) + (*right.children)[0] = (*left.children)[len(*left.children)-1] + (*left.children)[len(*left.children)-1] = nil + (*left.children) = (*left.children)[:len(*left.children)-1] + left.count -= (*right.children)[0].count + right.count += (*right.children)[0].count + } + } else { + // move left <- right over one slot + + // Same as above but the other direction + left.items = append(left.items, n.items[i]) + left.count++ + n.items[i] = right.items[0] + copy(right.items, right.items[1:]) + right.items[len(right.items)-1] = tr.empty + right.items = right.items[:len(right.items)-1] + right.count-- + + if !left.leaf() { + *left.children = append(*left.children, (*right.children)[0]) + copy(*right.children, (*right.children)[1:]) + (*right.children)[len(*right.children)-1] = nil + *right.children = (*right.children)[:len(*right.children)-1] + left.count += (*left.children)[len(*left.children)-1].count + right.count -= (*left.children)[len(*left.children)-1].count + } + } +} + +func (tr *Map[K, V]) ascend(pivot K, iter func(key K, value V) bool, mut bool) { + if tr.root == nil { + return + } + tr.nodeAscend(&tr.root, pivot, iter, mut) +} + +// The return value of this function determines whether we should keep iterating +// upon this functions return. +func (tr *Map[K, V]) nodeAscend(cn **mapNode[K, V], pivot K, + iter func(key K, value V) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + i, found := tr.search(n, pivot) + if !found { + if !n.leaf() { + if !tr.nodeAscend(&(*n.children)[i], pivot, iter, mut) { + return false + } + } + } + // We are either in the case that + // - node is found, we should iterate through it starting at `i`, + // the index it was located at. + // - node is not found, and TODO: fill in. + for ; i < len(n.items); i++ { + if !iter(n.items[i].key, n.items[i].value) { + return false + } + if !n.leaf() { + if !tr.nodeScan(&(*n.children)[i+1], iter, mut) { + return false + } + } + } + return true +} + +func (tr *Map[K, V]) reverse(iter func(key K, value V) bool, mut bool) { + if tr.root == nil { + return + } + tr.nodeReverse(&tr.root, iter, mut) +} + +func (tr *Map[K, V]) nodeReverse(cn **mapNode[K, V], + iter func(key K, value V) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + if n.leaf() { + for i := len(n.items) - 1; i >= 0; i-- { + if !iter(n.items[i].key, n.items[i].value) { + return false + } + } + return true + } + if !tr.nodeReverse(&(*n.children)[len(*n.children)-1], iter, mut) { + return false + } + for i := len(n.items) - 1; i >= 0; i-- { + if !iter(n.items[i].key, n.items[i].value) { + return false + } + if !tr.nodeReverse(&(*n.children)[i], iter, mut) { + return false + } + } + return true +} + +func (tr *Map[K, V]) descend( + pivot K, + iter func(key K, value V) bool, + mut bool, +) { + if tr.root == nil { + return + } + tr.nodeDescend(&tr.root, pivot, iter, mut) +} + +func (tr *Map[K, V]) nodeDescend(cn **mapNode[K, V], pivot K, + iter func(key K, value V) bool, mut bool, +) bool { + n := tr.isoLoad(cn, mut) + i, found := tr.search(n, pivot) + if !found { + if !n.leaf() { + if !tr.nodeDescend(&(*n.children)[i], pivot, iter, mut) { + return false + } + } + i-- + } + for ; i >= 0; i-- { + if !iter(n.items[i].key, n.items[i].value) { + return false + } + if !n.leaf() { + if !tr.nodeReverse(&(*n.children)[i], iter, mut) { + return false + } + } + } + return true +} + +func (tr *Map[K, V]) minMut(mut bool) (key K, value V, ok bool) { + if tr.root == nil { + return key, value, false + } + n := tr.isoLoad(&tr.root, mut) + for { + if n.leaf() { + item := n.items[0] + return item.key, item.value, true + } + n = tr.isoLoad(&(*n.children)[0], mut) + } +} + +func (tr *Map[K, V]) maxMut(mut bool) (K, V, bool) { + if tr.root == nil { + return tr.empty.key, tr.empty.value, false + } + n := tr.isoLoad(&tr.root, mut) + for { + if n.leaf() { + item := n.items[len(n.items)-1] + return item.key, item.value, true + } + n = tr.isoLoad(&(*n.children)[len(*n.children)-1], mut) + } +} + +func (tr *Map[K, V]) getAt(index int, mut bool) (K, V, bool) { + if tr.root == nil || index < 0 || index >= tr.count { + return tr.empty.key, tr.empty.value, false + } + n := tr.isoLoad(&tr.root, mut) + for { + if n.leaf() { + return n.items[index].key, n.items[index].value, true + } + i := 0 + for ; i < len(n.items); i++ { + if index < (*n.children)[i].count { + break + } else if index == (*n.children)[i].count { + return n.items[i].key, n.items[i].value, true + } + index -= (*n.children)[i].count + 1 + } + n = tr.isoLoad(&(*n.children)[i], mut) + } +} + +func (tr *Map[K, V]) values(mut bool) []V { + values := make([]V, 0, tr.Len()) + if tr.root != nil { + values = tr.nodeValues(&tr.root, values, mut) + } + return values +} + +func (tr *Map[K, V]) nodeValues(cn **mapNode[K, V], values []V, mut bool) []V { + n := tr.isoLoad(cn, mut) + if n.leaf() { + for i := 0; i < len(n.items); i++ { + values = append(values, n.items[i].value) + } + return values + } + for i := 0; i < len(n.items); i++ { + values = tr.nodeValues(&(*n.children)[i], values, mut) + values = append(values, n.items[i].value) + } + return tr.nodeValues(&(*n.children)[len(*n.children)-1], values, mut) +} + +func (n *mapNode[K, V]) keys(keys []K) []K { + if n.leaf() { + for i := 0; i < len(n.items); i++ { + keys = append(keys, n.items[i].key) + } + return keys + } + for i := 0; i < len(n.items); i++ { + keys = (*n.children)[i].keys(keys) + keys = append(keys, n.items[i].key) + } + return (*n.children)[len(*n.children)-1].keys(keys) +} + +func (tr *Map[K, V]) keyValues(mut bool) ([]K, []V) { + keys := make([]K, 0, tr.Len()) + values := make([]V, 0, tr.Len()) + if tr.root != nil { + keys, values = tr.nodeKeyValues(&tr.root, keys, values, mut) + } + return keys, values +} + +func (tr *Map[K, V]) nodeKeyValues(cn **mapNode[K, V], keys []K, values []V, + mut bool, +) ([]K, []V) { + n := tr.isoLoad(cn, mut) + if n.leaf() { + for i := 0; i < len(n.items); i++ { + keys = append(keys, n.items[i].key) + values = append(values, n.items[i].value) + } + return keys, values + } + for i := 0; i < len(n.items); i++ { + keys, values = tr.nodeKeyValues(&(*n.children)[i], keys, values, mut) + keys = append(keys, n.items[i].key) + values = append(values, n.items[i].value) + } + return tr.nodeKeyValues(&(*n.children)[len(*n.children)-1], keys, values, + mut) +} + +// iterator + +func (tr *Map[K, V]) iter(mut bool) MapIter[K, V] { + var iter MapIter[K, V] + iter.tr = tr + iter.mut = mut + return iter +} diff --git a/map_iter.go b/map_iter.go new file mode 100644 index 0000000..c41917f --- /dev/null +++ b/map_iter.go @@ -0,0 +1,202 @@ +package btree + +// MapIter represents an iterator for btree.Map +type MapIter[K ordered, V any] struct { + tr *Map[K, V] + mut bool + seeked bool + atstart bool + atend bool + stack []mapIterStackItem[K, V] + item mapPair[K, V] +} + +type mapIterStackItem[K ordered, V any] struct { + n *mapNode[K, V] + i int +} + +// Seek to item greater-or-equal-to key. +// Returns false if there was no item found. +func (iter *MapIter[K, V]) Seek(key K) bool { + if iter.tr == nil { + return false + } + iter.seeked = true + iter.stack = iter.stack[:0] + if iter.tr.root == nil { + return false + } + n := iter.tr.isoLoad(&iter.tr.root, iter.mut) + for { + i, found := iter.tr.search(n, key) + iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, i}) + if found { + iter.item = n.items[i] + return true + } + if n.leaf() { + iter.stack[len(iter.stack)-1].i-- + return iter.Next() + } + n = iter.tr.isoLoad(&(*n.children)[i], iter.mut) + } +} + +// First moves iterator to first item in tree. +// Returns false if the tree is empty. +func (iter *MapIter[K, V]) First() bool { + if iter.tr == nil { + return false + } + iter.atend = false + iter.atstart = false + iter.seeked = true + iter.stack = iter.stack[:0] + if iter.tr.root == nil { + return false + } + n := iter.tr.isoLoad(&iter.tr.root, iter.mut) + for { + iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, 0}) + if n.leaf() { + break + } + n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) + } + s := &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Last moves iterator to last item in tree. +// Returns false if the tree is empty. +func (iter *MapIter[K, V]) Last() bool { + if iter.tr == nil { + return false + } + iter.seeked = true + iter.stack = iter.stack[:0] + if iter.tr.root == nil { + return false + } + n := iter.tr.isoLoad(&iter.tr.root, iter.mut) + for { + iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, len(n.items)}) + if n.leaf() { + iter.stack[len(iter.stack)-1].i-- + break + } + n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) + } + s := &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Next moves iterator to the next item in iterator. +// Returns false if the tree is empty or the iterator is at the end of +// the tree. +func (iter *MapIter[K, V]) Next() bool { + if iter.tr == nil { + return false + } + if !iter.seeked { + return iter.First() + } + if len(iter.stack) == 0 { + if iter.atstart { + return iter.First() && iter.Next() + } + return false + } + s := &iter.stack[len(iter.stack)-1] + s.i++ + if s.n.leaf() { + if s.i == len(s.n.items) { + for { + iter.stack = iter.stack[:len(iter.stack)-1] + if len(iter.stack) == 0 { + iter.atend = true + return false + } + s = &iter.stack[len(iter.stack)-1] + if s.i < len(s.n.items) { + break + } + } + } + } else { + n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) + for { + iter.stack = append(iter.stack, mapIterStackItem[K, V]{n, 0}) + if n.leaf() { + break + } + n = iter.tr.isoLoad(&(*n.children)[0], iter.mut) + } + } + s = &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Prev moves iterator to the previous item in iterator. +// Returns false if the tree is empty or the iterator is at the beginning of +// the tree. +func (iter *MapIter[K, V]) Prev() bool { + if iter.tr == nil { + return false + } + if !iter.seeked { + return false + } + if len(iter.stack) == 0 { + if iter.atend { + return iter.Last() && iter.Prev() + } + return false + } + s := &iter.stack[len(iter.stack)-1] + if s.n.leaf() { + s.i-- + if s.i == -1 { + for { + iter.stack = iter.stack[:len(iter.stack)-1] + if len(iter.stack) == 0 { + iter.atstart = true + return false + } + s = &iter.stack[len(iter.stack)-1] + s.i-- + if s.i > -1 { + break + } + } + } + } else { + n := iter.tr.isoLoad(&(*s.n.children)[s.i], iter.mut) + for { + iter.stack = append(iter.stack, + mapIterStackItem[K, V]{n, len(n.items)}) + if n.leaf() { + iter.stack[len(iter.stack)-1].i-- + break + } + n = iter.tr.isoLoad(&(*n.children)[len(n.items)], iter.mut) + } + } + s = &iter.stack[len(iter.stack)-1] + iter.item = s.n.items[s.i] + return true +} + +// Key returns the current iterator item key. +func (iter *MapIter[K, V]) Key() K { + return iter.item.key +} + +// Value returns the current iterator item value. +func (iter *MapIter[K, V]) Value() V { + return iter.item.value +}