using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace PersistentOrderedMap { public static class BTreeFunctions { // --------------------------------------------------------- // Public API // --------------------------------------------------------- /// TryGetValue tries to get the value at mapping key. If it finds the key it sets th /// out var to value and returns true. public static bool TryGetValue(Node root, K key, TStrategy strategy, out V value) where TStrategy : IKeyStrategy { // We always get a strategy to avoid branching already here long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; Node current = root; while (true) { if (current.IsLeaf) { var leaf = current.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); if (index < leaf.Header.Count && strategy.Compare(leaf.Keys[index], key) == 0) { value = leaf.Values[index]; return true; } value = default!; return false; } else { var internalNode = current.AsInternal(); int index = FindRoutingIndex(internalNode, key, keyPrefix, strategy); current = internalNode.Children[index]!; } } } public static Node Set(Node root, K key, V value, IKeyStrategy strategy, OwnerId owner, out bool countChanged) { root = root.EnsureEditable(owner); // Todo, this should really be made a tuple return value to not stress the GC var splitResult = InsertRecursive(root, key, value, strategy, owner, out countChanged); if (splitResult != null) { var newRoot = strategy.UsesPrefixes ? new PrefixInternalNode(owner) : new InternalNode(owner); newRoot.Keys[0] = splitResult.Separator; newRoot.Children[0] = root; newRoot.Children[1] = splitResult.NewNode; newRoot.SetCount(1); if (strategy.UsesPrefixes) { newRoot.AllPrefixes[0] = strategy.GetPrefix(splitResult.Separator); } return newRoot; } return root; } private static SplitResult? InsertRecursive(Node node, K key, V value, IKeyStrategy strategy, OwnerId owner, out bool added) { long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; if (node.IsLeaf) { var leaf = node.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); if (index < leaf.Header.Count && strategy.Compare(leaf.Keys[index], key) == 0) { leaf.Values[index] = value; added = false; return null; } added = true; if (leaf.Header.Count < LeafNode.Capacity) { InsertIntoLeaf(leaf, index, key, value, strategy); return null; } else { return SplitLeaf(leaf, index, key, value, strategy, owner); } } else { var internalNode = node.AsInternal(); int index = FindRoutingIndex(internalNode, key, keyPrefix, strategy); var child = internalNode.Children[index]!.EnsureEditable(owner); internalNode.Children[index] = child; var split = InsertRecursive(child, key, value, strategy, owner, out added); if (split != null) { if (internalNode.Header.Count < InternalNode.Capacity - 1) { InsertIntoInternal(internalNode, index, split.Separator, split.NewNode, strategy); return null; } else { return SplitInternal(internalNode, index, split.Separator, split.NewNode, strategy, owner); } } return null; } } public static Node Remove(Node root, K key, TStrategy strategy, OwnerId owner, out bool countChanged) where TStrategy : IKeyStrategy { root = root.EnsureEditable(owner); bool rebalanceNeeded = RemoveRecursive(root, key, strategy, owner, out countChanged); if (rebalanceNeeded) { if (!root.IsLeaf) { var internalRoot = root.AsInternal(); if (internalRoot.Header.Count == 0) { return internalRoot.Children[0]!; } } } return root; } private static bool RemoveRecursive(Node node, K key, TStrategy strategy, OwnerId owner, out bool removed) where TStrategy : IKeyStrategy { long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; if (node.IsLeaf) { var leaf = node.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); if (index < leaf.Header.Count && strategy.Compare(leaf.Keys[index], key) == 0) { RemoveFromLeaf(leaf, index, strategy); removed = true; return leaf.Header.Count < LeafNode.MergeThreshold; } removed = false; return false; } else { var internalNode = node.AsInternal(); int index = FindRoutingIndex(internalNode, key, keyPrefix, strategy); var child = internalNode.Children[index]!.EnsureEditable(owner); internalNode.Children[index] = child; bool childUnderflow = RemoveRecursive(child, key, strategy, owner, out removed); if (removed && childUnderflow) { return HandleUnderflow(internalNode, index, strategy, owner); } return false; } } // --------------------------------------------------------- // Internal Helpers: Search // --------------------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int FindIndex(Node node, K key, long keyPrefix, TStrategy strategy) where TStrategy : IKeyStrategy { if (typeof(K) == typeof(int)) { Span keys = node.GetKeys(); ref K firstKeyRef = ref MemoryMarshal.GetReference(keys); ref int firstIntRef = ref Unsafe.As(ref firstKeyRef); ReadOnlySpan intKeys = MemoryMarshal.CreateReadOnlySpan(ref firstIntRef, keys.Length); int intKey = Unsafe.As(ref key); return IntScanner.FindFirstGreaterOrEqual(intKeys, intKey); } if (strategy.UsesPrefixes) { int index = PrefixScanner.FindFirstGreaterOrEqual(node.Prefixes, keyPrefix); return RefineSearch(index, node.GetKeys(), key, strategy); } return FallbackSearchKeys(node.GetKeys(), key, strategy); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int FindRoutingIndex(InternalNode node, K key, long keyPrefix, TStrategy strategy) where TStrategy : IKeyStrategy { if (typeof(K) == typeof(int)) { Span keys = node.GetKeys(); ref K firstKeyRef = ref MemoryMarshal.GetReference(keys); ref int firstIntRef = ref Unsafe.As(ref firstKeyRef); ReadOnlySpan intKeys = MemoryMarshal.CreateReadOnlySpan(ref firstIntRef, keys.Length); int intKey = Unsafe.As(ref key); return IntScanner.FindFirstGreater(intKeys, intKey); } if (!strategy.UsesPrefixes) { return FallbackRoutingKeys(node.GetKeys(), key, strategy); } int index = PrefixScanner.FindFirstGreaterOrEqual(node.Prefixes, keyPrefix); return RefineRouting(index, node.GetKeys(), key, strategy); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int RefineSearch(int startIndex, ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { int i = startIndex; while (i < keys.Length && strategy.Compare(keys[i], key) < 0) i++; return i; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int RefineRouting(int startIndex, ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { int i = startIndex; while (i < keys.Length && strategy.Compare(keys[i], key) <= 0) i++; return i; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int FallbackSearchKeys(ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { return strategy.UseBinarySearch ? BinarySearchKeys(keys, key, strategy) : LinearSearchKeys(keys, key, strategy); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int FallbackRoutingKeys(ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { return strategy.UseBinarySearch ? BinaryRoutingKeys(keys, key, strategy) : LinearRoutingKeys(keys, key, strategy); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LinearSearchKeys(ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { int i = 0; while (i < keys.Length && strategy.Compare(keys[i], key) < 0) i++; return i; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LinearRoutingKeys(ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { int i = 0; while (i < keys.Length && strategy.Compare(keys[i], key) <= 0) i++; return i; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int BinarySearchKeys(ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { int low = 0; int high = keys.Length - 1; ref K keysRef = ref MemoryMarshal.GetReference(keys); while (low <= high) { int mid = low + ((high - low) >> 1); K midKey = Unsafe.Add(ref keysRef, mid); int cmp = strategy.Compare(midKey, key); if (cmp == 0) return mid; if (cmp < 0) low = mid + 1; else high = mid - 1; } return low; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int BinaryRoutingKeys(ReadOnlySpan keys, K key, TStrategy strategy) where TStrategy : IKeyStrategy { int low = 0; int high = keys.Length - 1; ref K keysRef = ref MemoryMarshal.GetReference(keys); while (low <= high) { int mid = low + ((high - low) >> 1); K midKey = Unsafe.Add(ref keysRef, mid); int cmp = strategy.Compare(midKey, key); if (cmp <= 0) low = mid + 1; else high = mid - 1; } return low; } // --------------------------------------------------------- // Insertion Logic // --------------------------------------------------------- private class SeplitResult { public Node NewNode; public K Separator; public SeplitResult(Node newNode, K separator) { NewNode = newNode; Separator = separator; } } private static void InsertIntoLeaf(LeafNode leaf, int index, K key, V value, TStrategy strategy) where TStrategy : IKeyStrategy { int count = leaf.Header.Count; if (index < count) { int moveCount = count - index; leaf.Keys.AsSpan(index, moveCount).CopyTo(leaf.Keys.AsSpan(index + 1)); leaf.Values.AsSpan(index, moveCount).CopyTo(leaf.Values.AsSpan(index + 1)); if (strategy.UsesPrefixes) { leaf.AllPrefixes.Slice(index, moveCount).CopyTo(leaf.AllPrefixes.Slice(index + 1)); } } leaf.Keys[index] = key; leaf.Values[index] = value; if (strategy.UsesPrefixes) { leaf.AllPrefixes[index] = strategy.GetPrefix(key); } leaf.SetCount(count + 1); } private static (Node, K) SplitLeaf(LeafNode left, int insertIndex, K key, V value, TStrategy strategy, OwnerId owner) where TStrategy : IKeyStrategy { var right = new LeafNode(owner, strategy.UsesPrefixes); int totalCount = left.Header.Count; int splitPoint = (insertIndex == totalCount) ? totalCount : (insertIndex == 0 ? 0 : totalCount / 2); int moveCount = totalCount - splitPoint; if (moveCount > 0) { left.Keys.AsSpan(splitPoint, moveCount).CopyTo(right.Keys.AsSpan(0)); left.Values.AsSpan(splitPoint, moveCount).CopyTo(right.Values.AsSpan(0)); if (strategy.UsesPrefixes) { left.AllPrefixes.Slice(splitPoint, moveCount).CopyTo(right.AllPrefixes); } } left.SetCount(splitPoint); right.SetCount(moveCount); if (insertIndex < splitPoint || (splitPoint == 0 && insertIndex == 0)) { InsertIntoLeaf(left, insertIndex, key, value, strategy); } else { InsertIntoLeaf(right, insertIndex - splitPoint, key, value, strategy); } return (right, right.Keys[0]); } private static void InsertIntoInternal(InternalNode node, int index, K separator, Node newChild, TStrategy strategy) where TStrategy : IKeyStrategy { int count = node.Header.Count; if (index < count) { int moveCount = count - index; Span keysSpan = node.Keys; keysSpan.Slice(index, moveCount).CopyTo(keysSpan.Slice(index + 1)); Span> childrenSpan = node.Children; childrenSpan.Slice(index + 1, moveCount).CopyTo(childrenSpan.Slice(index + 2)); if (strategy.UsesPrefixes) { node.AllPrefixes.Slice(index, moveCount).CopyTo(node.AllPrefixes.Slice(index + 1)); } } node.Keys[index] = separator; node.Children[index + 1] = newChild; if (strategy.UsesPrefixes) { node.AllPrefixes[index] = strategy.GetPrefix(separator); } node.SetCount(count + 1); } private static (Node, K) SplitInternal(InternalNode left, int insertIndex, K separator, Node newChild, TStrategy strategy, OwnerId owner) where TStrategy : IKeyStrategy { var right = strategy.UsesPrefixes ? new PrefixInternalNode(owner) : new InternalNode(owner); int count = left.Header.Count; int splitPoint = count / 2; K upKey = left.Keys[splitPoint]; int moveCount = count - splitPoint - 1; if (moveCount > 0) { Span leftKeys = left.Keys; Span rightKeys = right.Keys; leftKeys.Slice(splitPoint + 1, moveCount).CopyTo(rightKeys); Span> leftChildren = left.Children; Span> rightChildren = right.Children; leftChildren.Slice(splitPoint + 1, moveCount + 1).CopyTo(rightChildren); if (strategy.UsesPrefixes) { left.AllPrefixes.Slice(splitPoint + 1, moveCount).CopyTo(right.AllPrefixes); } } left.SetCount(splitPoint); right.SetCount(moveCount); if (insertIndex <= splitPoint) { InsertIntoInternal(left, insertIndex, separator, newChild, strategy); } else { InsertIntoInternal(right, insertIndex - (splitPoint + 1), separator, newChild, strategy); } return (right, upKey); } // --------------------------------------------------------- // Removal Logic // --------------------------------------------------------- private static void RemoveFromLeaf(LeafNode leaf, int index, TStrategy strategy) where TStrategy : IKeyStrategy { int count = leaf.Header.Count; int moveCount = count - index - 1; if (moveCount > 0) { leaf.Keys.AsSpan(index + 1, moveCount).CopyTo(leaf.Keys.AsSpan(index)); leaf.Values.AsSpan(index + 1, moveCount).CopyTo(leaf.Values.AsSpan(index)); if (strategy.UsesPrefixes) { leaf.AllPrefixes.Slice(index + 1, moveCount).CopyTo(leaf.AllPrefixes.Slice(index)); } } leaf.SetCount(count - 1); } private static bool HandleUnderflow(InternalNode parent, int childIndex, TStrategy strategy, OwnerId owner) where TStrategy : IKeyStrategy { if (childIndex < parent.Header.Count) { var rightSibling = parent.Children[childIndex + 1]!.EnsureEditable(owner); parent.Children[childIndex + 1] = rightSibling; var leftChild = parent.Children[childIndex]!; if (CanBorrow(rightSibling)) { RotateLeft(parent, childIndex, leftChild, rightSibling, strategy); return false; } else { Merge(parent, childIndex, leftChild, rightSibling, strategy); return parent.Header.Count < LeafNode.MergeThreshold; } } else if (childIndex > 0) { var leftSibling = parent.Children[childIndex - 1]!.EnsureEditable(owner); parent.Children[childIndex - 1] = leftSibling; var rightChild = parent.Children[childIndex]!; if (CanBorrow(leftSibling)) { RotateRight(parent, childIndex - 1, leftSibling, rightChild, strategy); return false; } else { Merge(parent, childIndex - 1, leftSibling, rightChild, strategy); return parent.Header.Count < LeafNode.MergeThreshold; } } return true; } private static bool CanBorrow(Node node) { return node.Header.Count > 8 + 1; } private static void Merge(InternalNode parent, int separatorIndex, Node left, Node right, TStrategy strategy) where TStrategy : IKeyStrategy { if (left.IsLeaf) { var leftLeaf = left.AsLeaf(); var rightLeaf = right.AsLeaf(); int lCount = leftLeaf.Header.Count; int rCount = rightLeaf.Header.Count; rightLeaf.Keys.AsSpan(0, rCount).CopyTo(leftLeaf.Keys.AsSpan(lCount)); rightLeaf.Values.AsSpan(0, rCount).CopyTo(leftLeaf.Values.AsSpan(lCount)); if (strategy.UsesPrefixes) { rightLeaf.AllPrefixes.Slice(0, rCount).CopyTo(leftLeaf.AllPrefixes.Slice(lCount)); } leftLeaf.SetCount(lCount + rCount); } else { var leftInternal = left.AsInternal(); var rightInternal = right.AsInternal(); K separator = parent.Keys[separatorIndex]; int lCount = leftInternal.Header.Count; leftInternal.Keys[lCount] = separator; if (strategy.UsesPrefixes) { leftInternal.AllPrefixes[lCount] = strategy.GetPrefix(separator); } int rCount = rightInternal.Header.Count; Span rightKeys = rightInternal.Keys; Span leftKeys = leftInternal.Keys; rightKeys.Slice(0, rCount).CopyTo(leftKeys.Slice(lCount + 1)); if (strategy.UsesPrefixes) { rightInternal.AllPrefixes.Slice(0, rCount).CopyTo(leftInternal.AllPrefixes.Slice(lCount + 1)); } Span> rightChildren = rightInternal.Children; Span> leftChildren = leftInternal.Children; rightChildren.Slice(0, rCount + 1).CopyTo(leftChildren.Slice(lCount + 1)); leftInternal.SetCount(lCount + 1 + rCount); } int pCount = parent.Header.Count; int moveCount = pCount - separatorIndex - 1; if (moveCount > 0) { Span parentKeys = parent.Keys; parentKeys.Slice(separatorIndex + 1, moveCount).CopyTo(parentKeys.Slice(separatorIndex)); if (strategy.UsesPrefixes) { parent.AllPrefixes.Slice(separatorIndex + 1, moveCount).CopyTo(parent.AllPrefixes.Slice(separatorIndex)); } Span> parentChildren = parent.Children; parentChildren.Slice(separatorIndex + 2, moveCount).CopyTo(parentChildren.Slice(separatorIndex + 1)); } parent.SetCount(pCount - 1); } private static void RotateLeft(InternalNode parent, int separatorIndex, Node left, Node right, TStrategy strategy) where TStrategy : IKeyStrategy { if (left.IsLeaf) { var leftLeaf = left.AsLeaf(); var rightLeaf = right.AsLeaf(); InsertIntoLeaf(leftLeaf, leftLeaf.Header.Count, rightLeaf.Keys[0], rightLeaf.Values[0], strategy); RemoveFromLeaf(rightLeaf, 0, strategy); parent.Keys[separatorIndex] = rightLeaf.Keys[0]; if (strategy.UsesPrefixes) { parent.AllPrefixes[separatorIndex] = strategy.GetPrefix(rightLeaf.Keys[0]); } } else { var leftInternal = left.AsInternal(); var rightInternal = right.AsInternal(); K sep = parent.Keys[separatorIndex]; InsertIntoInternal(leftInternal, leftInternal.Header.Count, sep, rightInternal.Children[0]!, strategy); parent.Keys[separatorIndex] = rightInternal.Keys[0]; if (strategy.UsesPrefixes) { parent.AllPrefixes[separatorIndex] = strategy.GetPrefix(rightInternal.Keys[0]); } int rCount = rightInternal.Header.Count; Span> rightChildren = rightInternal.Children; rightChildren.Slice(1, rCount).CopyTo(rightChildren); if (rCount > 1) { Span rightKeys = rightInternal.Keys; rightKeys.Slice(1, rCount - 1).CopyTo(rightKeys); if (strategy.UsesPrefixes) { rightInternal.AllPrefixes.Slice(1, rCount - 1).CopyTo(rightInternal.AllPrefixes); } } rightInternal.SetCount(rCount - 1); } } private static void RotateRight(InternalNode parent, int separatorIndex, Node left, Node right, TStrategy strategy) where TStrategy : IKeyStrategy { if (left.IsLeaf) { var leftLeaf = left.AsLeaf(); var rightLeaf = right.AsLeaf(); int last = leftLeaf.Header.Count - 1; InsertIntoLeaf(rightLeaf, 0, leftLeaf.Keys[last], leftLeaf.Values[last], strategy); RemoveFromLeaf(leftLeaf, last, strategy); parent.Keys[separatorIndex] = rightLeaf.Keys[0]; if (strategy.UsesPrefixes) { parent.AllPrefixes[separatorIndex] = strategy.GetPrefix(rightLeaf.Keys[0]); } } else { var leftInternal = left.AsInternal(); var rightInternal = right.AsInternal(); int last = leftInternal.Header.Count - 1; K sep = parent.Keys[separatorIndex]; InsertIntoInternal(rightInternal, 0, sep, leftInternal.Children[last + 1]!, strategy); parent.Keys[separatorIndex] = leftInternal.Keys[last]; if (strategy.UsesPrefixes) { parent.AllPrefixes[separatorIndex] = strategy.GetPrefix(leftInternal.Keys[last]); } leftInternal.SetCount(last); } } public static bool TryGetMin(Node root, out K key, out V value) { var current = root; while (!current.IsLeaf) { current = current.AsInternal().Children[0]!; } var leaf = current.AsLeaf(); if (leaf.Header.Count == 0) { key = default!; value = default!; return false; } key = leaf.Keys[0]; value = leaf.Values[0]; return true; } public static bool TryGetMax(Node root, out K key, out V value) { var current = root; while (!current.IsLeaf) { var internalNode = current.AsInternal(); current = internalNode.Children[internalNode.Header.Count]!; } var leaf = current.AsLeaf(); if (leaf.Header.Count == 0) { key = default!; value = default!; return false; } int last = leaf.Header.Count - 1; key = leaf.Keys[last]; value = leaf.Values[last]; return true; } public static bool TryGetSuccessor(Node root, K key, TStrategy strategy, out K nextKey, out V nextValue) where TStrategy : IKeyStrategy { InternalNode[] path = new InternalNode[32]; int[] indices = new int[32]; int depth = 0; long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; var current = root; while (!current.IsLeaf) { var internalNode = current.AsInternal(); int idx = FindRoutingIndex(internalNode, key, keyPrefix, strategy); path[depth] = internalNode; indices[depth] = idx; depth++; current = internalNode.Children[idx]!; } var leaf = current.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); if (index < leaf.Header.Count && strategy.Compare(leaf.Keys[index], key) == 0) index++; if (index < leaf.Header.Count) { nextKey = leaf.Keys[index]; nextValue = leaf.Values[index]; return true; } for (int i = depth - 1; i >= 0; i--) { if (indices[i] < path[i].Header.Count) { current = path[i].Children[indices[i] + 1]!; while (!current.IsLeaf) { current = current.AsInternal().Children[0]!; } var targetLeaf = current.AsLeaf(); nextKey = targetLeaf.Keys[0]; nextValue = targetLeaf.Values[0]; return true; } } nextKey = default!; nextValue = default!; return false; } public static bool TryGetPredecessor(Node root, K key, TStrategy strategy, out K prevKey, out V prevValue) where TStrategy : IKeyStrategy { InternalNode[] path = new InternalNode[32]; int[] indices = new int[32]; int depth = 0; long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; var current = root; while (!current.IsLeaf) { var internalNode = current.AsInternal(); int idx = FindRoutingIndex(internalNode, key, keyPrefix, strategy); path[depth] = internalNode; indices[depth] = idx; depth++; current = internalNode.Children[idx]!; } var leaf = current.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); if (index > 0) { prevKey = leaf.Keys[index - 1]; prevValue = leaf.Values[index - 1]; return true; } for (int i = depth - 1; i >= 0; i--) { if (indices[i] > 0) { current = path[i].Children[indices[i] - 1]!; while (!current.IsLeaf) { var internalNode = current.AsInternal(); current = internalNode.Children[internalNode.Header.Count]!; } var targetLeaf = current.AsLeaf(); int last = targetLeaf.Header.Count - 1; prevKey = targetLeaf.Keys[last]; prevValue = targetLeaf.Values[last]; return true; } } prevKey = default!; prevValue = default!; return false; } } }