/* * Copyright 2025 Casper Dalgaard Nielsen * Adam Yasser Tallouzi * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */////// Type of mutable concurrent B+ trees, and its functions.///pubmod BPlusTree {import java.lang.Runtime////// Type of mutable concurrent B+ trees.////// The tree maps keys of type `k` to values of type `v`.////// A B+ Tree is an m-ary self-balancing search tree where each node is either an internal/// node or a leaf node. In contrast to binary search trees, a B+ Tree of arity `m` only/// allows nodes with at most `m` children and `m-1` keys. A B+ Tree stores its keys in/// a sorted order in its internal nodes, which are used to guide searches to leaf nodes/// that store the keys' corresponding values.////// See below for more details:////// - The Ubiquitous B-Tree (1979) by Douglas Comer/// (https://dl.acm.org/doi/10.1145/356770.356776)////// A BPlusTree satisfies the following.////// `root` is the root node of the tree, which upon creation of the tree is a leaf node./// Through insertion into the tree, a new node may be created and propagated up to become/// the new root of the tree.////// `arity` or `m` is the arity of the tree which determines the maximum number of children/// a node may have. The arity must be at least 3.////// `rootLock` is a lock on the `root` pointer, which a thread must first acquire before/// accessing the root node. It must also be acquired before changing `root`. The node/// `root` has a separate lock concerning the contents of the node. The lock of `root`/// must first be acquired after successfully acquiring `rootLock`.////// `rc` is the region for the tree.////// `search` is only used internally within the Fixpoint module where keys are of type/// `Vector[Int64]`. A `search` describes the order in which elements should be/// compared. E.g. [0, 1, 2, ..., n] is the natural order. A `search` may only be used/// when using `Vector[Int64]` as keys.////// `counter` is used for keeping track of the number of inserted elements.////// For all other usages `search` will be a vector of size 0.////// Concurrency: thread-safe operations use optimistic stamps and validation./// Functions marked "Not thread-safe" traverse or mutate the live tree without a stable/// snapshot, so concurrent writes may affect the observed result.///pubstructBPlusTree[k, v, r] {mut root: BPlusTree.Node[k, v, r], arity: Int32, rootLock: BPlusTree.Lock[r], rc: Region[r], counter: BPlusTree.AtomicCounter[r], search: BPlusTree.Search }/// Describes the order in which the elements of vector keys are compared; empty for non-vector keys.pubtypealiasSearch = Vector[Int32]instanceForEach[BPlusTree[k, v, r]] {typeElm = (k, v)typeAef = rpubdefforEach(f: ((k, v)) -> Unit \ ef, m: BPlusTree[k, v, r]): Unit \ ef + r = BPlusTree.forEach(k -> v -> f((k, v)), m) }////// Updates `t` with `k => f(v)` if `k => v` is in `t`.////// Otherwise, `t` is unchanged.////// Thread-safe.///pubdefadjust(f: v -> v \ ef, k: k, t: BPlusTree[k, v, r]): Unit \ ef + rwithOrder[k] =adjustWithKey(_ -> v -> f(v), k, t)////// Updates `t` with `k => f(k, v)` if `k => v` is in `t`.////// Otherwise, `t` is unchanged.////// Thread-safe.///pubdefadjustWithKey(f: k -> v -> v \ ef, k: k, t: BPlusTree[k, v, r]): Unit \ ef + rwithOrder[k] = {let (node, stamp) = findLeaf(k, t);Node.adjustWithKey(f, k, stamp, t, node) }////// Returns true if and only if `t` is a structurally correct tree.////// Only used for internal testing.///pubdefassertTreeInvariant(t: BPlusTree[k, v, r]): Bool \ rwithOrder[k] = {letroot = t->root;letarity = BPlusTree.arity(t);letnodeWorks = Node.assertNodeInvariant(root, arity, true, None, t->search);letrootIsUnlocked = notBPlusTree.Lock.isLocked(t->rootLock);letrootHasNoParent = Option.isEmpty(Node.parent(root));nodeWorksandrootHasNoParentandrootIsUnlocked }////// Returns the number of mappings in `t` that satisfy the predicate function `f`.////// This method unsafely removes the effect `r` when spawning threads.////// Not thread-safe: Traverses the live leaf chain of `t` in parallel.///@Parallelpubdefcount(f: k -> v -> Bool, t: BPlusTree[k, v, r]): Int32 \ rwithOrder[k] = {letc = AtomicCounter.mkCounter(t->rc, 0);parForEach(k -> v -> if (f(k, v)) AtomicCounter.increment(c) else(), t);AtomicCounter.get(c) }////// Alias for `foldLeftWithKey`///pubdeffoldWithKey(f: b -> k -> v -> b \ ef, i: b, t: BPlusTree[k, v, r]): b \ r + efwithOrder[k] =foldLeftWithKey(f, i, t)////// Returns `v` if `k => v` is in `t`.////// Otherwise updates `t` with a new mapping `k => d` and returns `d`.////// Atomic and thread-safe///pubdefgetOrElsePut(k: k, d: v, t: BPlusTree[k, v, r]): v \ rwithOrder[k] =computeIfAbsent(_ -> d, k, t)////// Returns `true` if `t1` and `t2` contain the same keys and values, regardless of/// each individual tree's internal structure.////// Not thread-safe: Compares the live leaf chains of `t1` and `t2`.///pubdefsameElements(t1: BPlusTree[k, v, r1], t2: BPlusTree[k, v, r2]): Bool \ r1 + r2withOrder[k], Eq[v] = {if (size(t1) !=size(t2))falseelseNode.sameElementsFromLeaf(0, Node.leftMostLeafUnconditional(t1->root), 0, Node.leftMostLeafUnconditional(t2->root)) }////// Applies the function `f` to every value in `t`.////// Not thread-safe: Mutates the live leaf chain of `t` in place.///pubdeftransform(f: v -> v \ ef, t: BPlusTree[k, v, r]): Unit \ r + efwithOrder[k] =transformWithKey(_ -> v -> f(v), t)////// Applies the function `f` to every mapping `k => v` in `t`, replacing it with/// `k => f(k, v)`.////// Not thread-safe: Mutates the live leaf chain of `t` in place.///pubdeftransformWithKey(f: k -> v -> v \ ef, t: BPlusTree[k, v, r]): Unit \ r + efwithOrder[k] =Node.transformWithKeyFromLeaf(f, 0, Node.leftMostLeafUnconditional(t->root))////// Returns `v` if `k => v` is in `t`. Otherwise, computes `v = f()`, inserts `k => v`/// and returns `v`. That is, `f` is only evaluated if `k` is not in `t`.////// This operation is atomic.////// Thread-safe.///pubdefcomputeIfAbsent(f: Unit -> v \ ef, k: k, t: BPlusTree[k, v, r]): v \ ef + rwithOrder[k] = {let (leaf, stamp) = findLeaf(k, t);matchNode.get(k, stamp, leaf, t) {case Some(v) => vcase None =>letlock = Node.getLock(leaf);Lock.withWriteLockOrElse(stamp,lock,writeStamp -> {letv = f();fixRoot(Node.insertIntoLeaf(k, v, leaf, writeStamp, t), t);v },() -> computeIfAbsent(f, k, t) ) } }////// Returns `true` if and only if for all `k => v` in `t`, `f(k, v) == true`.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdefforAll(f: k -> v -> Bool \ ef, t: BPlusTree[k, v, r]): Bool \ ef + r =notexists(k -> v -> notf(k, v), t)////// Returns `true` if and only if there exists `k => v` in `t` such that `f(k, v) == true`.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdefexists(f: k -> v -> Bool \ ef, t: BPlusTree[k, v, r]): Bool \ ef + r =Node.existsFromLeaf(f, 0, Node.leftMostLeafUnconditional(t->root))////// Alias for `findLeft`.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdeffind(f: k -> v -> Bool \ ef, t: BPlusTree[k, v, r]): Option[(k, v)] \ ef + r =findLeft(f, t)////// Optionally returns the first mapping in `t` satisfying the function `f`.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdeffindLeft(f: k -> v -> Bool \ ef, t: BPlusTree[k, v, r]): Option[(k, v)] \ ef + r =Node.findLeftFromLeaf(f, 0, Node.leftMostLeafUnconditional(t->root))////// Increments the stored size of `t`.////// Thread-safe.///defincrementSize(t: BPlusTree[k, v, r]): Unit \ r = AtomicCounter.increment(t->counter)////// Returns `true` if and only if `t` contains the mapping `k => v`.////// Thread-safe.///pubdefmemberOfPair(k: k, v: v, t: BPlusTree[k, v, r]): Bool \ rwithOrder[k], Eq[v] = {let (leaf, stamp) = BPlusTree.findLeaf(k, t);letres = Node.leafMemberOfPair(k, v, t->search, leaf);Lock.ifValidElse(stamp, Node.getLock(leaf), () -> res, () -> memberOfPair(k, v, t)) }////// Returns a `BPlusTree` with mappings `k => f(v)` for every `k => v` in `t`.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdefmap(rc1: Region[r1], f: v1 -> v2 \ ef, t: BPlusTree[k, v1, r]): BPlusTree[k, v2, r1] \ ef + r + r1withOrder[k] =mapWithKey(rc1, _ -> v -> f(v), t)////// Returns a `BPlusTree` with mappings `k => f(k, v)` for every `k => v` in `t`.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdefmapWithKey(rc1: Region[r1], f: k -> v1 -> v2 \ ef, t: BPlusTree[k, v1, r]): BPlusTree[k, v2, r1] \ ef + r + r1withOrder[k] = {lett1 = BPlusTree.emptyWithArity(rc1, BPlusTree.arity(t));foreach ((k, v) <- t) {BPlusTree.put(k, f(k, v), t1) };t1 }////// Returns an iterator over keys in `t`.////// Not thread-safe: The iterator reads from the live leaf chain of `t`.///pubdefiteratorWithKeys(rc1: Region[r1], t: BPlusTree[k, v, r2]): Iterator[k, r1 + r2, r1] \ r1 + r2 =Iterator.map(match (k, _) -> k, iterator(rc1, t))////// Returns an iterator over values in `t`.////// Not thread-safe: The iterator reads from the live leaf chain of `t`.///pubdefiteratorValues(rc1: Region[r1], t: BPlusTree[k, v, r2]): Iterator[v, r1 + r2, r1] \ r1 + r2 =Iterator.map(match (_, v) -> v, iterator(rc1, t))////// Returns an iterator over all key-value pairs in `t`,////// Not thread-safe: The iterator reads from the live leaf chain of `t`.///pubdefiterator(rc1: Region[r1], t: BPlusTree[k, v, r]): Iterator[(k, v), r + r1, r1] \ r + r1 = {letnodeRef = Ref.fresh(rc1, Node.leftMostLeafUnconditional(t->root));letindexRef = Ref.fresh(rc1, 0);defnext() = {letnode = Ref.get(nodeRef);letindex = Ref.get(indexRef);if (index<Node.size(node)) {Ref.put(index+1, indexRef); Some(Node.getKeyAndValueAt(index, node)) } elsematchNode.next(node) {case None => Nonecase Some(nextNode) => {Ref.put(nextNode, nodeRef);Ref.put(0, indexRef);next() } } };Iterator.unfoldWithIter(rc1, next) }////// Returns `t` as an immutable map.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdeftoMap(t: BPlusTree[k, v, r]): Map[k, v] \ rwithOrder[k] = (Map.empty(), t) ||> foldLeftWithKey(acc -> k -> v -> Map.insert(k, v, acc))////// Returns `t` as a set of key-value pairs.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdeftoSet(t: BPlusTree[k, v, r]): Set[(k, v)] \ rwithOrder[k], Order[v] = (Set.empty(), t) ||> foldLeftWithKey(acc -> k -> v -> Set.insert((k, v), acc))////// Returns `t` as a vector of key-value pairs.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdeftoVector(t: BPlusTree[k, v, r]): Vector[(k, v)] \ r = regionrc {letmm = MutList.empty(rc);foreach ((k, v) <- t) {MutList.push((k, v), mm) };MutList.toVector(mm) }////// Returns a fresh empty BPlusTree tree of standard arity.///pubdefempty(rc: Region[r]): BPlusTree[k, v, r] \ r =emptyWithSearch(rc, unchecked_cast(nullasVector[Int32]))////// Returns a fresh empty BPlusTree with arity `m`. A node may have at most `m`/// children and `m - 1` keys./// The arity must be at least 3.///pubdefemptyWithArity(rc: Region[r], m: Int32): BPlusTree[k, v, r] \ r =emptyWithArityAndSearch(rc, m`Int32.max`3, unchecked_cast(nullasVector[Int32]))////// Returns a fresh empty BPlusTree with arity `m`. A node may have at most `m`/// children and `m - 1` keys./// The arity must be at least 3.////// `search` decides the comparison order for the stored vectors.///pubdefemptyWithArityAndSearch(rc: Region[r], m: Int32, search: Search): BPlusTree[k, v, r] \ r =letnode = Node.mkLeaf(rc, Array.empty(rc, m), Array.empty(rc, m), 0, None, None);new BPlusTree @ rc { root = node, arity = m, rootLock = Lock.mkLock(rc), rc = rc, search = search, counter = AtomicCounter.mkCounter(rc, 0) }////// Returns a fresh empty BPlusTree of standard arity.////// `search` decided the comparison order for the stored vectors.///pubdefemptyWithSearch(rc: Region[r], search: Search): BPlusTree[k, v, r] \ r =emptyWithArityAndSearch(rc, 64, search)////// Returns the leaf in `t` containing key `k` together with an optimistic stamp on that leaf.////// If no node contains `k`, the rightmost node with values strictly less than `k` is returned.////// The returned stamp must still be validated or upgraded before relying on data read from the/// returned leaf.///deffindLeaf(k: k, t: BPlusTree[k, v, r]): (Node[k, v, r], Int64) \ rwithOrder[k] = {letsearch = BPlusTree.search(t);let (node, stamp) = getLockedRoot(t);Node.traverseToLeaf(node, stamp, n -> Node.childForKey(k, search, n), () -> BPlusTree.findLeaf(k, t)) }////// Updates the root of `t` if `newRoot` is `Some(...)`. Unlocks `t->rootLock` using the stamp.///deffixRoot(newRoot: Option[(Node[k, v, r], Int64)], t: BPlusTree[k, v, r]): Unit \ r = matchnewRoot {case Some((root, rootStamp)) =>t->root = root;Lock.unlockWrite(rootStamp, t->rootLock);()case None => () }////// Applies `f` to a start value `i` and all values in `t` going from left to right.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdeffoldLeft(f: b -> v -> b \ ef, i: b, t: BPlusTree[k, v, r]): b \ ef + rwithOrder[k] = {letminLeaf = Node.leftMostLeafUnconditional(t->root);Node.foldLeftWithKeyFromLeaf(acc -> _ -> v -> f(acc, v), i, 0, minLeaf) }////// Applies `f` to a start value `i` and all mappings `k => v` in `t` going from left to right.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdeffoldLeftWithKey(f: b -> k -> v -> b \ ef, i: b, t: BPlusTree[k, v, r]): b \ ef + rwithOrder[k] = {letminLeaf = Node.leftMostLeafUnconditional(t->root);Node.foldLeftWithKeyFromLeaf(f, i, 0, minLeaf) }////// Applies `f` to all mappings `k => v` in `t`.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdefforEach(f: k -> v -> Unit \ ef, t: BPlusTree[k, v, r]): Unit \ ef + r = {letminLeaf = Node.leftMostLeafUnconditional(t->root);Node.traverseRightUnconditional(f, 0, minLeaf) }////// Returns the current root of `t` together with an optimistic stamp on that root node.////// The root pointer is validated before returning, but the returned node stamp is still only/// optimistic and must be validated by the caller before trusting reads derived from `rootNode`.///defgetLockedRoot(t: BPlusTree[k, v, r]): (Node[k, v, r], Int64) \ r = {letrootPointerStamp = Lock.tryReadLock(t->rootLock);letrootNode = t->root;letnodeStamp = Lock.tryReadLock(Node.getLock(rootNode));Lock.ifValidElse(rootPointerStamp, t->rootLock, () -> (rootNode, nodeStamp), () -> getLockedRoot(t)) }////// Runs `descend` from the locked root of `t`.///deffromLockedRoot(t: BPlusTree[k, v, r],descend: Node[k, v, r] -> Int64 -> (Node[k, v, r], Int64) \ r ): (Node[k, v, r], Int64) \ rwithOrder[k] = {let (node, stamp) = getLockedRoot(t);descend(node, stamp) }////// Returns the first mapping in `leaf`, if any.///deffirstEntryInLeaf(leaf: Node[k, v, r]): Option[(k, v)] \ r = matchNode.size(leaf) {case 0 => Nonecase_ => Some(Node.getKeyAndValueAt(0, leaf)) }////// Returns the last mapping in `leaf`, if any.///deflastEntryInLeaf(leaf: Node[k, v, r]): Option[(k, v)] \ r = matchNode.size(leaf) {case 0 => Nonecasen => Some(Node.getKeyAndValueAt(n-1, leaf)) }////// Returns `Some(v)` if `k => v` is in `t`. Otherwise, returns `None`.////// Thread-safe.///pubdefget(k: k, t: BPlusTree[k, v, r]): Option[v] \ rwithOrder[k] = {let (leaf, stamp) = BPlusTree.findLeaf(k, t);Node.get(k, stamp, leaf, t) }////// Returns `Some((k, v))` if `k => v` is in `t`. Otherwise, returns `None`.////// Thread-safe.///pubdefgetKeyAndValue(k: k, t: BPlusTree[k, v, r]): Option[(k, v)] \ rwithOrder[k] = {let (leaf, stamp) = BPlusTree.findLeaf(k, t);Node.getKeyAndValue(k, stamp, leaf, t) }////// Returns `v` if `k => v` is in `t`. Otherwise, returns `d`.////// Thread-safe.///pubdefgetWithDefault(k: k, d: v, t: BPlusTree[k, v, r]): v \ rwithOrder[k] = {let (leaf, stamp) = BPlusTree.findLeaf(k, t);Node.getWithDefault(k, stamp, d, leaf, t) }////// Returns the set of keys in `t`.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdefkeysOf(t: BPlusTree[k, v, r]): Set[k] \ rwithOrder[k] = regionrc {letset = MutSet.empty(rc);forEach(k -> _ -> MutSet.add(k, set), t);MutSet.toSet(set) }////// Returns the list of values in `t`.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdefvaluesOf(t: BPlusTree[k, v, r]): List[v] \ rwithOrder[k] = regionrc {letlist = MutList.empty(rc);forEach(_ -> v -> MutList.push(v, list), t);MutList.toList(list) }////// Returns a string containing all values in `t`, in key order,/// with each value formatted by its `ToString` instance and separated by `sep`.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdefjoinValues(sep: String, t: BPlusTree[k, v, r]): String \ rwithToString[v] =joinWith(_ -> v -> ToString.toString(v), sep, t)////// Returns a string containing all keys in `t`, in key order,/// with each key formatted by its `ToString` instance and separated by `sep`.////// Not thread-safe.////// Builds the result by traversing the live leaf chain of `t`.///pubdefjoinKeys(sep: String, t: BPlusTree[k, v, r]): String \ rwithToString[k] =joinWith(k -> _ -> ToString.toString(k), sep, t)////// Applies `f` to each key-value pair in `t`, in key order, to obtain a/// string and returns all results joined together using `sep`.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdefjoinWith(f: k -> v -> String \ ef, sep: String, t: BPlusTree[k, v, r]): String \ r + ef = regionrc {letsb = StringBuilder.empty(rc);foreach ((k, v) <- t) {StringBuilder.append(f(k, v), sb);StringBuilder.append(sep, sb) };letstr = StringBuilder.toString(sb);String.dropRight(String.length(sep), str) }////// Applies `f` to all mappings `k => v` in `t` in parallel./// `f` must not modify `t`.////// This method unsafe removes the `r` effect when spawning threads.////// Not thread-safe: Traverses the live leaf chain of `t` in parallel.///@ParallelpubdefparForEach(f: k -> v -> Unit \ r, t: BPlusTree[k, v, r]): Unit \ r = regionrc {// `threadNum` is the number of spawned threads.letthreadNum = if (Node.computeHeight(t->root) >=2) {threads() } else {Int32.min(threads(), Node.size(t->root)) };if (threadNum<=1) {forEach(f, t) } else {letminLeaf = Node.leftMostLeafUnconditional(t->root);unsafeIOasr {Vector.range(0, threadNum) |> Vector.forEach(i -> {let_: Unit = spawnunsaferasIO {// Flix does not allow region effect in spawns. Remove the effect.Node.traverseRightUnconditionalInc(f, i, threadNum, minLeaf) } @ rc;() } ) } } }////// Applies `f1` to all mappings `k => v` in `t` sequentially if `t` contains strictly less than/// `parLimit` elements. If `t` contains `parLimit` or more elements `f2` is applied in parallel/// to all mappings.////// `f1` and `f2` must not modify `t`.////// This method unsafe removes the `r` effect when spawning threads in case 2.////// Not thread-safe: Traverses the live leaf chain of `t`, potentially in parallel.///@ParallelpubdefparForEachWhen(f1: k -> v -> Unit \ r, f2: k -> v -> Unit \ r, parLimit: Int32, t: BPlusTree[k, v, r]): Unit \ r = {if (size(t) <parLimit)forEach(f1, t)elseparForEach(f2, t) }////// Updates the tree `t` with the mapping `k => v`, replacing any existing mapping.////// Thread-safe.///pubdefput(k: k, v: v, t: BPlusTree[k, v, r]): Unit \ rwithOrder[k] =fixRoot(putInternal(k, v, t), t)////// Updates the tree `t` with the mapping `k => v`, replacing any existing mapping,/// `k2 => v2`, if `decider(k, v, k2, v2) == true`.////// Thread-safe and atomic.///pubdefputIf(decider: k -> v -> k -> v -> Bool, k: k, v: v, t: BPlusTree[k, v, r]): Unit \ rwithOrder[k] =fixRoot(putIfInternal(decider, k, v, t), t)////// Internal method for `putIf`.////// Called when restarting.////// Returns the `Some(newRoot, stamp)` if the root was split where `stamp` is a write-stamp/// on `rootLock`.///defputIfInternal(decider: k -> v -> k -> v -> Bool, k: k, v: v, t: BPlusTree[k, v, r]): Option[(Node[k, v, r], Int64)] \ rwithOrder[k] = {let (leaf, stamp) = BPlusTree.findLeaf(k, t);Node.insertIntoLeafIf(decider, k, v, leaf, stamp, t) }////// Internal method for `put`.////// Called when restarting.////// Returns the `Some(newRoot, stamp)` if the root was split where `stamp` is a write-stamp/// on `rootLock`.///defputInternal(k: k, v: v, t: BPlusTree[k, v, r]): Option[(Node[k, v, r], Int64)] \ rwithOrder[k] = {let (leaf, stamp) = BPlusTree.findLeaf(k, t);Node.insertIntoLeaf(k, v, leaf, stamp, t) }////// Updates the tree `t` with all mappings `k => v` in `kvs`, replacing any existing mappings.////// Thread-safe.///pubdefputAll(kvs: f[(k, v)], t: BPlusTree[k, v, r]): Unit \ r + Foldable.Aef[f] withOrder[k], Foldable[f] =Foldable.forEach(match (k, v) -> put(k, v, t), kvs)////// Applies `f` to all mappings `k => v` in `t`, passing an index as the first argument.////// Not thread-safe: Traverses the live leaf chain of `t`.///pubdefforEachWithIndex(f: Int32 -> k -> v -> Unit \ ef, t: BPlusTree[k, v, r]): Unit \ ef + r = {letminLeaf = Node.leftMostLeafUnconditional(t->root);Node.traverseRightUnconditionalWithIndex(f, 0, 0, minLeaf) }////// Returns `v` if `k => v` is in `t`. Aborts with a trace message if `k` is not in `t`.////// Thread-safe.///pubdefgetOrAbort(k: k, t: BPlusTree[k, v, r]): v \ Abort + rwithOrder[k] = {let (leaf, stamp) = findLeaf(k, t);matchNode.get(k, stamp, leaf, t) {case None => Abort.abortWithTrace("BPlusTree.getOrAbort(): key not found")case Some(v) => v } }////// Returns `true` if and only if `t` is the empty tree.////// Thread-safe.///pubdefisEmpty(t: BPlusTree[k, v, r]): Bool \ r = {let (root, stamp) = BPlusTree.getLockedRoot(t);letres = Node.isEmpty(root);Lock.ifValidElse(stamp, Node.getLock(root), () -> res, () -> isEmpty(t)) }////// Returns `true` if and only if `t` contains at least one mapping.////// Thread-safe.///pubdefnonEmpty(t: BPlusTree[k, v, r]): Bool \ r =notBPlusTree.isEmpty(t)////// Returns `true` if and only if `t` contains the key `k`.////// Thread-safe.///pubdefmemberOf(k: k, t: BPlusTree[k, v, r]): Bool \ rwithOrder[k] = {let (leaf, stamp) = BPlusTree.findLeaf(k, t);letres = Node.leafMemberOf(k, t->search, leaf);Lock.ifValidElse(stamp, Node.getLock(leaf), () -> res, () -> memberOf(k, t)) }////// Merge `src` into `dst`, modifying `dst` in a left-biased manner.////// That is, key collisions are resolved by taking the mapping from `src`.////// Not thread-safe: Traverses the live leaf chain of `src`.///pubdefmerge(src: BPlusTree[k, v, r1], dst: BPlusTree[k, v, r2]): Unit \ r1 + r2withOrder[k] =BPlusTree.forEach(k -> v -> BPlusTree.put(k, v, dst), src)////// Merges `src` into `dst`, modifying `dst`. If `k => v1` is in `src` and `k => v2` is/// in `dst`, updates `dst` with `k => f(v1, v2)`.////// Not thread-safe: Traverses the live leaf chain of `src`.///pubdefmergeWith(f: v -> v -> v \ ef, src: BPlusTree[k, v, r1], dst: BPlusTree[k, v, r2]): Unit \ r1 + r2 + efwithOrder[k] =BPlusTree.forEach(k -> v -> BPlusTree.putWith(f, k, v, dst), src)////// Merges `src` into `dst`, modifying `dst` in-place. If `k => v1` is in `src` and/// `k => v2` is in `dst`, then `dst` is updated with `k => f(k, v1, v2)`.////// Not thread-safe: Traverses the live leaf chain of `src`.///pubdefmergeWithKey(f: k -> v -> v -> v \ ef, src: BPlusTree[k, v, r1], dst: BPlusTree[k, v, r2]): Unit \ r1 + r2 + efwithOrder[k] =forEach(k -> v -> BPlusTree.putWithKey(f, k, v, dst), src)////// Returns true if `t1` is a submap of `t2`, i.e. for every key in `t1`, `t2`/// contains the same key with the same value.////// Not thread-safe: Traverses the live leaf chains of `t1` and `t2`.///pubdefisSubmapOf(t1: BPlusTree[k, v, r1], t2: BPlusTree[k, v, r2]): Bool \ r1 + r2withOrder[k], Eq[v] = regionrc {if (size(t1) >size(t2)) {false } else {// Future optimization: when t1 is much smaller than t2, it might be faster to iterate// over t1 and check membership in t2 instead of iterating over both trees in parallelleti1 = iterator(rc, t1);leti2 = iterator(rc, t2);defcheckRemaining(m1, m2) = match (m1, m2) {case (Some((k1, v1)), Some((k2, v2))) => {if (k1<k2)falseelseif (k1>k2)checkRemaining(m1, Iterator.next(i2))elseif (v1!=v2)falseelsecheckRemaining(Iterator.next(i1), Iterator.next(i2)) }case (Some(_), None) => falsecase (None, _) => true };checkRemaining(Iterator.next(i1), Iterator.next(i2)) } }////// Returns true if `t1` is a proper submap of `t2`, i.e. `t1` is a submap of `t2` and/// there exists a key in `t2` that is not in `t1`.////// Not thread-safe: Traverses the live leaf chains of `t1` and `t2`.///pubdefisProperSubmapOf(t1: BPlusTree[k, v, r1], t2: BPlusTree[k, v, r2]): Bool \ r1 + r2withOrder[k], Eq[v] =size(t1) <size(t2) andisSubmapOf(t1, t2)////// Optionally returns `k => v` where `k` is the smallest key in `t` according to the/// `Order` instance on `k`.////// Returns `None` if `t` is empty.////// Thread-safe.///pubdefminimumKey(t: BPlusTree[k, v, r]): Option[(k, v)] \ rwithOrder[k] = {let (leaf, leafStamp) = leftMostLeaf(t);letresult = firstEntryInLeaf(leaf);Lock.ifValidElse(leafStamp, Node.getLock(leaf), () -> result, () -> minimumKey(t)) }////// Optionally returns `k => v` where `k` is the largest key in `t` according to the/// `Order` instance on `k`.////// Returns `None` if `t` is empty.////// Thread-safe.///pubdefmaximumKey(t: BPlusTree[k, v, r]): Option[(k, v)] \ rwithOrder[k] = {let (leaf, leafStamp) = rightMostLeaf(t);letresult = lastEntryInLeaf(leaf);Lock.ifValidElse(leafStamp, Node.getLock(leaf), () -> result, () -> maximumKey(t)) }////// Returns the leftmost leaf of `t` and an optimistic stamp on that leaf.////// The leftmost leaf contains the minimum key in the tree.///defleftMostLeaf(t: BPlusTree[k, v, r]): (Node[k, v, r], Int64) \ rwithOrder[k] =fromLockedRoot(t, node -> stamp -> Node.leftMostLeaf(node, stamp, t))////// Returns the rightmost leaf of `t` and an optimistic stamp on that leaf.////// The rightmost leaf contains the maximum key in the tree.///defrightMostLeaf(t: BPlusTree[k, v, r]): (Node[k, v, r], Int64) \ rwithOrder[k] =fromLockedRoot(t, node -> stamp -> Node.rightMostLeaf(node, stamp, t))////// Returns the arity of `t`.///pubdefarity(t: BPlusTree[k, v, r]): Int32 = t->arity////// Updates `t` with `k => f(v, v1)` if `k => v1` is in `t`.////// Otherwise, updates `t` with `k => v`.////// Thread-safe.///pubdefputWith(f: v -> v -> v \ ef, k: k, v: v, t: BPlusTree[k, v, r]): Unit \ ef + rwithOrder[k] =fixRoot(putWithInternal(f, k, v, t), t)////// Internal method to update the mapping as described by `putWith`.////// Called when restarting.////// Thread-safe.///defputWithInternal(f: v -> v -> v \ ef, k: k, v: v, t: BPlusTree[k, v, r]): Option[(Node[k, v, r], Int64)] \ ef + rwithOrder[k] = {let (leaf, stamp) = findLeaf(k, t);Node.putWith(f, k, v, t, leaf, stamp) }////// Updates `t` with the mapping `k => f(k, v, v1)` if `k => v1` is in `t`.////// Otherwise, updates `t` with the mapping `k => v`.////// Thread-safe.///pubdefputWithKey(f: k -> v -> v -> v \ ef, k: k, v: v, t: BPlusTree[k, v, r]): Unit \ r + efwithOrder[k] =fixRoot(putWithKeyInternal(f, k, v, t), t)////// Internal method to update the mapping as described by `putWithKey`.////// Called when restarting.////// Thread-safe.///defputWithKeyInternal(f: k -> v -> v -> v \ ef, k: k, v: v, t: BPlusTree[k, v, r]): Option[(Node[k, v, r], Int64)] \ r + efwithOrder[k] = {let (leaf, stamp) = findLeaf(k, t);Node.putWithKey(f, k, v, t, stamp, leaf) }////// Returns the list of mappings `k => v` in `t` where `min <= k <= max` according to/// the `Order` instance on `k`.////// Not thread-safe: Builds the result by traversing the live leaf chain of `t` in range order.///pubdefrangeQuery(min: k, max: k, t: BPlusTree[k, v, r]): List[(k, v)] \ rwithOrder[k] = regionrc {letl = MutList.empty(rc);rangeQueryWith(k -> v -> MutList.push((k, v), l), min, max, t);MutList.toList(l) }////// Applies `f` in ascending order to all mappings `k => v` in `t` where/// `min <= k <= max`.////// Not thread-safe: Traverses the live leaf chain of `t` in range order.///pubdefrangeQueryWith(f: k -> v -> Unit \ ef, min: k, max: k, t: BPlusTree[k, v, r]): Unit \ r + efwithOrder[k] = {let (root, stamp) = getLockedRoot(t);Node.rangeQueryWith(f, min, max, stamp, root, t) }////// Returns the region of `t`.///pubdefrc(t: BPlusTree[k, v, r]): Region[r] = t->rc////// Returns the search of `t`.///pubdefsearch(t: BPlusTree[k, v, r]): Search = t->search////// Returns the size of `t`. This operation is `O(1)`.////// Not atomic.////// Thread-safe.///pubdefsize(t: BPlusTree[k, v, r]): Int32 \ r = AtomicCounter.get(t->counter)////// Returns the number of threads to use for parallel evaluation.////// # SAFETY:/// This accesses the runtime environment, which is an effect./// It is assumed that this function is only used in contexts/// where this effect is not observable outside of the BPlusTree module.///defthreads(): Int32 = {// Note: We use a multiple of the number of physical cores for better performance.letmultiplier = 4;multiplier* Runtime.getRuntime().availableProcessors() }////// Returns a list of the key-value pairs in `t`. Elements are ordered from smallest/// (left) to largest (right).////// Not thread-safe: Builds the result by traversing the live leaf chain of `t`.///pubdeftoList(t: BPlusTree[k, v, r]): List[(k, v)] \ r = regionrc {letlist = MutList.empty(rc);forEach(k -> v -> MutList.push((k, v), list), t);MutList.toList(list) }////// Returns a string representation of `t`.////// Not thread-safe: Traverses the live tree without synchronization.///pubdeftoString(t: BPlusTree[k, v, r]): String \ rwithToString[k], ToString[v] =Node.toString(0, t->rc, t->root)////// Write lock `t->rootLock` and return the stamp.///defwriteLockRootPointer(t: BPlusTree[k, v, r]): Int64 \ r =Lock.writeLock(t->rootLock)////// Unlock the root write lock of `t`.///defunlockRootPointer(stamp: Int64, t: BPlusTree[k, v, r]): Unit \ r =Lock.unlockWrite(stamp, t->rootLock)}