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