From 23c4ac299e4a0bf9ba5935344b24f1e338701d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Bj=C3=B6rnstam?= Date: Thu, 21 May 2026 13:13:22 +0200 Subject: [PATCH] surpressed all style warnings from the LSP server. --- PersistentOrderedMap.sln | 7 - PersistentOrderedMap/BTreeFunctions.cs | 298 +++++++++--------- PersistentOrderedMap/BaseOrderedMap.cs | 70 ++-- PersistentOrderedMap/Iterator.cs | 76 ++--- PersistentOrderedMap/KeyStrategies.cs | 10 +- .../KeyStrategies/ComparableStrategy.cs | 6 +- .../KeyStrategies/StandardStrategy.cs | 22 +- PersistentOrderedMap/Nodes.cs | 92 +++--- PersistentOrderedMap/PersistentOrderedMap.cs | 27 +- PersistentOrderedMap/TransientOrderedMap.cs | 17 +- TestProject1/FuzzTest.cs | 34 +- TestProject1/FuzzTestStandardStrategy.cs | 34 +- TestProject1/StandardStrategy.cs | 24 +- 13 files changed, 346 insertions(+), 371 deletions(-) diff --git a/PersistentOrderedMap.sln b/PersistentOrderedMap.sln index d13d62d..f313444 100644 --- a/PersistentOrderedMap.sln +++ b/PersistentOrderedMap.sln @@ -10,8 +10,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgainstImmutableDict", "benchmarks\AgainstImmutableDict\AgainstImmutableDict.csproj", "{13304F19-7ED3-4C40-9A08-46D539667D50}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgainstLanguageExt", "benchmarks\AgainstLanguageExt\AgainstLanguageExt.csproj", "{6C16526B-5139-4EA3-BF74-E6320F467198}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyBenchMarks", "benchmarks\MyBenchMarks\MyBenchMarks.csproj", "{769E1CEA-7E01-405B-80A2-95CBF432A2BA}" EndProject Global @@ -22,7 +20,6 @@ Global GlobalSection(NestedProjects) = preSolution {CA49AA3C-0CE6-4735-887F-FB3631D63CEE} = {B0432C7A-80E2-4EA6-8FAB-B8F23A8C39DE} {13304F19-7ED3-4C40-9A08-46D539667D50} = {E38B3FCB-0D4D-401D-A2FC-EDF41B755E53} - {6C16526B-5139-4EA3-BF74-E6320F467198} = {E38B3FCB-0D4D-401D-A2FC-EDF41B755E53} {769E1CEA-7E01-405B-80A2-95CBF432A2BA} = {E38B3FCB-0D4D-401D-A2FC-EDF41B755E53} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution @@ -38,10 +35,6 @@ Global {13304F19-7ED3-4C40-9A08-46D539667D50}.Debug|Any CPU.Build.0 = Debug|Any CPU {13304F19-7ED3-4C40-9A08-46D539667D50}.Release|Any CPU.ActiveCfg = Release|Any CPU {13304F19-7ED3-4C40-9A08-46D539667D50}.Release|Any CPU.Build.0 = Release|Any CPU - {6C16526B-5139-4EA3-BF74-E6320F467198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6C16526B-5139-4EA3-BF74-E6320F467198}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6C16526B-5139-4EA3-BF74-E6320F467198}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6C16526B-5139-4EA3-BF74-E6320F467198}.Release|Any CPU.Build.0 = Release|Any CPU {769E1CEA-7E01-405B-80A2-95CBF432A2BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {769E1CEA-7E01-405B-80A2-95CBF432A2BA}.Debug|Any CPU.Build.0 = Debug|Any CPU {769E1CEA-7E01-405B-80A2-95CBF432A2BA}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/PersistentOrderedMap/BTreeFunctions.cs b/PersistentOrderedMap/BTreeFunctions.cs index 34c2222..0d7a7af 100644 --- a/PersistentOrderedMap/BTreeFunctions.cs +++ b/PersistentOrderedMap/BTreeFunctions.cs @@ -11,20 +11,20 @@ namespace PersistentOrderedMap /// 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 + public static bool TryGetValue(Node root, TK key, TStrategy strategy, out TV 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; + Node current = root; while (true) { if (current.IsLeaf) { - var leaf = current.AsLeaf(); + var leaf = current.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); - if (index < leaf.Header.Count && strategy.Compare(leaf.Keys[index], key) == 0) + if (index < leaf.Header.Count && strategy.Compare(leaf.Keys![index], key) == 0) { value = leaf.Values[index]; return true; @@ -41,8 +41,8 @@ namespace PersistentOrderedMap } } - public static Node Set(Node root, K key, V value, TStrategy strategy, OwnerId owner, out bool countChanged) - where TStrategy : IKeyStrategy + public static Node Set(Node root, TK key, TV value, TStrategy strategy, OwnerId owner, out bool countChanged) + where TStrategy : IKeyStrategy { root = root.EnsureEditable(owner); @@ -52,8 +52,8 @@ namespace PersistentOrderedMap if (newNode != null) { var newRoot = strategy.UsesPrefixes - ? new PrefixInternalNode(owner) - : new InternalNode(owner); + ? new PrefixInternalNode(owner) + : new InternalNode(owner); newRoot.Keys[0] = sep; newRoot.Children[0] = root; @@ -71,27 +71,27 @@ namespace PersistentOrderedMap return root; } - private static (Node? newNode, K separator ) InsertRecursive(Node node, K key, V value, IKeyStrategy strategy, OwnerId owner, out bool added) + private static (Node? newNode, TK separator ) InsertRecursive(Node node, TK key, TV value, IKeyStrategy strategy, OwnerId owner, out bool added) { long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; if (node.IsLeaf) { - var leaf = node.AsLeaf(); + var leaf = node.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); - if (index < leaf.Header.Count && strategy.Compare(leaf.Keys[index], key) == 0) + if (index < leaf.Header.Count && strategy.Compare(leaf.Keys![index], key) == 0) { leaf.Values[index] = value; added = false; - return (null, default); + return (null, default)!; } added = true; - if (leaf.Header.Count < LeafNode.Capacity) + if (leaf.Header.Count < LeafNode.Capacity) { InsertIntoLeaf(leaf, index, key, value, strategy); - return (null, default); + return (null, default)!; } else { @@ -110,26 +110,25 @@ namespace PersistentOrderedMap if (newNode != null) { - if (internalNode.Header.Count < InternalNode.Capacity - 1) + if (internalNode.Header.Count < InternalNode.Capacity - 1) { InsertIntoInternal(internalNode, index, sep, newNode, strategy); - return (null, default); - } - else - { - return SplitInternal(internalNode, index, sep, newNode, strategy, owner); + return (null, default)!; } + + return SplitInternal(internalNode, index, sep, newNode, strategy, owner); + } - return (null, default); + return (null, default)!; } } - public static Node Remove(Node root, K key, TStrategy strategy, OwnerId owner, out bool countChanged) - where TStrategy : IKeyStrategy + public static Node Remove(Node root, TK key, TStrategy strategy, OwnerId owner, out bool countChanged) + where TStrategy : IKeyStrategy { root = root.EnsureEditable(owner); - bool rebalanceNeeded = RemoveRecursive(root, key, strategy, owner, out countChanged); + bool rebalanceNeeded = RemoveRecursive(root, key, strategy, owner, out countChanged); if (rebalanceNeeded) { @@ -146,21 +145,21 @@ namespace PersistentOrderedMap return root; } - private static bool RemoveRecursive(Node node, K key, TStrategy strategy, OwnerId owner, out bool removed) - where TStrategy : IKeyStrategy + private static bool RemoveRecursive(Node node, TK 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(); + var leaf = node.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); - if (index < leaf.Header.Count && strategy.Compare(leaf.Keys[index], key) == 0) + if (index < leaf.Header.Count && strategy.Compare(leaf.Keys![index], key) == 0) { RemoveFromLeaf(leaf, index, strategy); removed = true; - return leaf.Header.Count < LeafNode.MergeThreshold; + return leaf.Header.Count < LeafNode.MergeThreshold; } removed = false; @@ -174,11 +173,11 @@ namespace PersistentOrderedMap var child = internalNode.Children[index]!.EnsureEditable(owner); internalNode.Children[index] = child; - bool childUnderflow = RemoveRecursive(child, key, strategy, owner, out removed); + bool childUnderflow = RemoveRecursive(child, key, strategy, owner, out removed); if (removed && childUnderflow) { - return HandleUnderflow(internalNode, index, strategy, owner); + return HandleUnderflow(internalNode, index, strategy, owner); } return false; } @@ -189,16 +188,16 @@ namespace PersistentOrderedMap // --------------------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int FindIndex(LeafNode node, K key, long keyPrefix, TStrategy strategy) - where TStrategy : IKeyStrategy + internal static int FindIndex(LeafNode node, TK key, long keyPrefix, TStrategy strategy) + where TStrategy : IKeyStrategy { - if (typeof(K) == typeof(int)) + if (typeof(TK) == typeof(int)) { - Span keys = node.GetKeys(); - ref K firstKeyRef = ref MemoryMarshal.GetReference(keys); - ref int firstIntRef = ref Unsafe.As(ref firstKeyRef); + Span keys = node.GetKeys(); + ref TK 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); + int intKey = Unsafe.As(ref key); return IntScanner.FindFirstGreaterOrEqual(intKeys, intKey); } @@ -212,17 +211,17 @@ namespace PersistentOrderedMap } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int FindRoutingIndex(InternalNode node, K key, long keyPrefix, TStrategy strategy) - where TStrategy : IKeyStrategy + internal static int FindRoutingIndex(InternalNode node, TK key, long keyPrefix, TStrategy strategy) + where TStrategy : IKeyStrategy { - if (typeof(K) == typeof(int)) + if (typeof(TK) == typeof(int)) { - Span keys = node.GetKeys(); - ref K firstKeyRef = ref MemoryMarshal.GetReference(keys); - ref int firstIntRef = ref Unsafe.As(ref firstKeyRef); + Span keys = node.GetKeys(); + ref TK 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); + int intKey = Unsafe.As(ref key); return IntScanner.FindFirstGreater(intKeys, intKey); } if (!strategy.UsesPrefixes) @@ -235,8 +234,8 @@ namespace PersistentOrderedMap } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RefineSearch(int startIndex, ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int RefineSearch(int startIndex, ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { int i = startIndex; while (i < keys.Length && strategy.Compare(keys[i], key) < 0) i++; @@ -244,8 +243,8 @@ namespace PersistentOrderedMap } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RefineRouting(int startIndex, ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int RefineRouting(int startIndex, ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { int i = startIndex; while (i < keys.Length && strategy.Compare(keys[i], key) <= 0) i++; @@ -253,8 +252,8 @@ namespace PersistentOrderedMap } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int FallbackSearchKeys(ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int FallbackSearchKeys(ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { return strategy.UseBinarySearch ? BinarySearchKeys(keys, key, strategy) @@ -262,8 +261,8 @@ namespace PersistentOrderedMap } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int FallbackRoutingKeys(ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int FallbackRoutingKeys(ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { return strategy.UseBinarySearch ? BinaryRoutingKeys(keys, key, strategy) @@ -271,8 +270,8 @@ namespace PersistentOrderedMap } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int LinearSearchKeys(ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int LinearSearchKeys(ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { int i = 0; while (i < keys.Length && strategy.Compare(keys[i], key) < 0) i++; @@ -280,8 +279,8 @@ namespace PersistentOrderedMap } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int LinearRoutingKeys(ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int LinearRoutingKeys(ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { int i = 0; while (i < keys.Length && strategy.Compare(keys[i], key) <= 0) i++; @@ -290,17 +289,17 @@ namespace PersistentOrderedMap [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int BinarySearchKeys(ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int BinarySearchKeys(ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { int low = 0; int high = keys.Length - 1; - ref K keysRef = ref MemoryMarshal.GetReference(keys); + ref TK keysRef = ref MemoryMarshal.GetReference(keys); while (low <= high) { int mid = low + ((high - low) >> 1); - K midKey = Unsafe.Add(ref keysRef, mid); + TK midKey = Unsafe.Add(ref keysRef, mid); int cmp = strategy.Compare(midKey, key); if (cmp == 0) return mid; @@ -310,17 +309,17 @@ namespace PersistentOrderedMap return low; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int BinaryRoutingKeys(ReadOnlySpan keys, K key, TStrategy strategy) - where TStrategy : IKeyStrategy + private static int BinaryRoutingKeys(ReadOnlySpan keys, TK key, TStrategy strategy) + where TStrategy : IKeyStrategy { int low = 0; int high = keys.Length - 1; - ref K keysRef = ref MemoryMarshal.GetReference(keys); + ref TK keysRef = ref MemoryMarshal.GetReference(keys); while (low <= high) { int mid = low + ((high - low) >> 1); - K midKey = Unsafe.Add(ref keysRef, mid); + TK midKey = Unsafe.Add(ref keysRef, mid); int cmp = strategy.Compare(midKey, key); if (cmp <= 0) low = mid + 1; @@ -333,19 +332,8 @@ namespace PersistentOrderedMap // 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 + private static void InsertIntoLeaf(LeafNode leaf, int index, TK key, TV value, TStrategy strategy) + where TStrategy : IKeyStrategy { int count = leaf.Header.Count; if (index < count) @@ -360,7 +348,7 @@ namespace PersistentOrderedMap } } - leaf.Keys[index] = key; + leaf.Keys![index] = key; leaf.Values[index] = value; if (strategy.UsesPrefixes) @@ -371,10 +359,10 @@ namespace PersistentOrderedMap leaf.SetCount(count + 1); } - private static (Node, K) SplitLeaf(LeafNode left, int insertIndex, K key, V value, TStrategy strategy, OwnerId owner) - where TStrategy : IKeyStrategy + private static (Node, TK) SplitLeaf(LeafNode left, int insertIndex, TK key, TV value, TStrategy strategy, OwnerId owner) + where TStrategy : IKeyStrategy { - var right = new LeafNode(owner, strategy.UsesPrefixes); + var right = new LeafNode(owner, strategy.UsesPrefixes); int totalCount = left.Header.Count; int splitPoint = (insertIndex == totalCount) ? totalCount : (insertIndex == 0 ? 0 : totalCount / 2); @@ -403,21 +391,21 @@ namespace PersistentOrderedMap InsertIntoLeaf(right, insertIndex - splitPoint, key, value, strategy); } - return (right, right.Keys[0]); + return (right, right.Keys![0]); } - private static void InsertIntoInternal(InternalNode node, int index, K separator, Node newChild, TStrategy strategy) - where TStrategy : IKeyStrategy + private static void InsertIntoInternal(InternalNode node, int index, TK separator, Node newChild, TStrategy strategy) + where TStrategy : IKeyStrategy { int count = node.Header.Count; if (index < count) { int moveCount = count - index; - Span keysSpan = node.Keys; + Span keysSpan = node.Keys; keysSpan.Slice(index, moveCount).CopyTo(keysSpan.Slice(index + 1)); - Span> childrenSpan = node.Children; + Span> childrenSpan = node.Children!; childrenSpan.Slice(index + 1, moveCount).CopyTo(childrenSpan.Slice(index + 2)); if (strategy.UsesPrefixes) @@ -437,26 +425,26 @@ namespace PersistentOrderedMap node.SetCount(count + 1); } - private static (Node, K) SplitInternal(InternalNode left, int insertIndex, K separator, Node newChild, TStrategy strategy, OwnerId owner) - where TStrategy : IKeyStrategy + private static (Node, TK) SplitInternal(InternalNode left, int insertIndex, TK separator, Node newChild, TStrategy strategy, OwnerId owner) + where TStrategy : IKeyStrategy { var right = strategy.UsesPrefixes - ? new PrefixInternalNode(owner) - : new InternalNode(owner); + ? new PrefixInternalNode(owner) + : new InternalNode(owner); int count = left.Header.Count; int splitPoint = count / 2; - K upKey = left.Keys[splitPoint]; + TK upKey = left.Keys[splitPoint]; int moveCount = count - splitPoint - 1; if (moveCount > 0) { - Span leftKeys = left.Keys; - Span rightKeys = right.Keys; + Span leftKeys = left.Keys; + Span rightKeys = right.Keys; leftKeys.Slice(splitPoint + 1, moveCount).CopyTo(rightKeys); - Span> leftChildren = left.Children; - Span> rightChildren = right.Children; + Span> leftChildren = left.Children!; + Span> rightChildren = right.Children!; leftChildren.Slice(splitPoint + 1, moveCount + 1).CopyTo(rightChildren); if (strategy.UsesPrefixes) @@ -484,8 +472,8 @@ namespace PersistentOrderedMap // Removal Logic // --------------------------------------------------------- - private static void RemoveFromLeaf(LeafNode leaf, int index, TStrategy strategy) - where TStrategy : IKeyStrategy + private static void RemoveFromLeaf(LeafNode leaf, int index, TStrategy strategy) + where TStrategy : IKeyStrategy { int count = leaf.Header.Count; int moveCount = count - index - 1; @@ -504,8 +492,8 @@ namespace PersistentOrderedMap leaf.SetCount(count - 1); } - private static bool HandleUnderflow(InternalNode parent, int childIndex, TStrategy strategy, OwnerId owner) - where TStrategy : IKeyStrategy + private static bool HandleUnderflow(InternalNode parent, int childIndex, TStrategy strategy, OwnerId owner) + where TStrategy : IKeyStrategy { if (childIndex < parent.Header.Count) { @@ -515,13 +503,13 @@ namespace PersistentOrderedMap if (CanBorrow(rightSibling)) { - RotateLeft(parent, childIndex, leftChild, rightSibling, strategy); + RotateLeft(parent, childIndex, leftChild, rightSibling, strategy); return false; } else { - Merge(parent, childIndex, leftChild, rightSibling, strategy); - return parent.Header.Count < LeafNode.MergeThreshold; + Merge(parent, childIndex, leftChild, rightSibling, strategy); + return parent.Header.Count < LeafNode.MergeThreshold; } } else if (childIndex > 0) @@ -532,31 +520,31 @@ namespace PersistentOrderedMap if (CanBorrow(leftSibling)) { - RotateRight(parent, childIndex - 1, leftSibling, rightChild, strategy); + RotateRight(parent, childIndex - 1, leftSibling, rightChild, strategy); return false; } else { - Merge(parent, childIndex - 1, leftSibling, rightChild, strategy); - return parent.Header.Count < LeafNode.MergeThreshold; + Merge(parent, childIndex - 1, leftSibling, rightChild, strategy); + return parent.Header.Count < LeafNode.MergeThreshold; } } return true; } - private static bool CanBorrow(Node node) + 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 + 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(); + var leftLeaf = left.AsLeaf(); + var rightLeaf = right.AsLeaf(); int lCount = leftLeaf.Header.Count; int rCount = rightLeaf.Header.Count; @@ -576,7 +564,7 @@ namespace PersistentOrderedMap var leftInternal = left.AsInternal(); var rightInternal = right.AsInternal(); - K separator = parent.Keys[separatorIndex]; + TK separator = parent.Keys[separatorIndex]; int lCount = leftInternal.Header.Count; leftInternal.Keys[lCount] = separator; @@ -587,8 +575,8 @@ namespace PersistentOrderedMap } int rCount = rightInternal.Header.Count; - Span rightKeys = rightInternal.Keys; - Span leftKeys = leftInternal.Keys; + Span rightKeys = rightInternal.Keys; + Span leftKeys = leftInternal.Keys; rightKeys.Slice(0, rCount).CopyTo(leftKeys.Slice(lCount + 1)); if (strategy.UsesPrefixes) @@ -596,8 +584,8 @@ namespace PersistentOrderedMap rightInternal.AllPrefixes.Slice(0, rCount).CopyTo(leftInternal.AllPrefixes.Slice(lCount + 1)); } - Span> rightChildren = rightInternal.Children; - Span> leftChildren = leftInternal.Children; + Span> rightChildren = rightInternal.Children!; + Span> leftChildren = leftInternal.Children!; rightChildren.Slice(0, rCount + 1).CopyTo(leftChildren.Slice(lCount + 1)); leftInternal.SetCount(lCount + 1 + rCount); @@ -608,7 +596,7 @@ namespace PersistentOrderedMap if (moveCount > 0) { - Span parentKeys = parent.Keys; + Span parentKeys = parent.Keys; parentKeys.Slice(separatorIndex + 1, moveCount).CopyTo(parentKeys.Slice(separatorIndex)); if (strategy.UsesPrefixes) @@ -616,22 +604,22 @@ namespace PersistentOrderedMap parent.AllPrefixes.Slice(separatorIndex + 1, moveCount).CopyTo(parent.AllPrefixes.Slice(separatorIndex)); } - Span> parentChildren = parent.Children; + 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 + 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(); + var leftLeaf = left.AsLeaf(); + var rightLeaf = right.AsLeaf(); - InsertIntoLeaf(leftLeaf, leftLeaf.Header.Count, rightLeaf.Keys[0], rightLeaf.Values[0], strategy); + InsertIntoLeaf(leftLeaf, leftLeaf.Header.Count, rightLeaf.Keys![0], rightLeaf.Values[0], strategy); RemoveFromLeaf(rightLeaf, 0, strategy); parent.Keys[separatorIndex] = rightLeaf.Keys[0]; @@ -645,7 +633,7 @@ namespace PersistentOrderedMap var leftInternal = left.AsInternal(); var rightInternal = right.AsInternal(); - K sep = parent.Keys[separatorIndex]; + TK sep = parent.Keys[separatorIndex]; InsertIntoInternal(leftInternal, leftInternal.Header.Count, sep, rightInternal.Children[0]!, strategy); parent.Keys[separatorIndex] = rightInternal.Keys[0]; @@ -656,12 +644,12 @@ namespace PersistentOrderedMap int rCount = rightInternal.Header.Count; - Span> rightChildren = rightInternal.Children; + Span> rightChildren = rightInternal.Children!; rightChildren.Slice(1, rCount).CopyTo(rightChildren); if (rCount > 1) { - Span rightKeys = rightInternal.Keys; + Span rightKeys = rightInternal.Keys; rightKeys.Slice(1, rCount - 1).CopyTo(rightKeys); if (strategy.UsesPrefixes) @@ -674,19 +662,19 @@ namespace PersistentOrderedMap } } - private static void RotateRight(InternalNode parent, int separatorIndex, Node left, Node right, TStrategy strategy) - where TStrategy : IKeyStrategy + 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(); + var leftLeaf = left.AsLeaf(); + var rightLeaf = right.AsLeaf(); int last = leftLeaf.Header.Count - 1; - InsertIntoLeaf(rightLeaf, 0, leftLeaf.Keys[last], leftLeaf.Values[last], strategy); + InsertIntoLeaf(rightLeaf, 0, leftLeaf.Keys![last], leftLeaf.Values[last], strategy); RemoveFromLeaf(leftLeaf, last, strategy); - parent.Keys[separatorIndex] = rightLeaf.Keys[0]; + parent.Keys[separatorIndex] = rightLeaf.Keys![0]; if (strategy.UsesPrefixes) { parent.AllPrefixes[separatorIndex] = strategy.GetPrefix(rightLeaf.Keys[0]); @@ -698,7 +686,7 @@ namespace PersistentOrderedMap var rightInternal = right.AsInternal(); int last = leftInternal.Header.Count - 1; - K sep = parent.Keys[separatorIndex]; + TK sep = parent.Keys[separatorIndex]; InsertIntoInternal(rightInternal, 0, sep, leftInternal.Children[last + 1]!, strategy); parent.Keys[separatorIndex] = leftInternal.Keys[last]; @@ -711,7 +699,7 @@ namespace PersistentOrderedMap } } - public static bool TryGetMin(Node root, out K key, out V value) + public static bool TryGetMin(Node root, out TK key, out TV value) { var current = root; while (!current.IsLeaf) @@ -719,7 +707,7 @@ namespace PersistentOrderedMap current = current.AsInternal().Children[0]!; } - var leaf = current.AsLeaf(); + var leaf = current.AsLeaf(); if (leaf.Header.Count == 0) { key = default!; @@ -727,12 +715,12 @@ namespace PersistentOrderedMap return false; } - key = leaf.Keys[0]; + key = leaf.Keys![0]; value = leaf.Values[0]; return true; } - public static bool TryGetMax(Node root, out K key, out V value) + public static bool TryGetMax(Node root, out TK key, out TV value) { var current = root; while (!current.IsLeaf) @@ -741,7 +729,7 @@ namespace PersistentOrderedMap current = internalNode.Children[internalNode.Header.Count]!; } - var leaf = current.AsLeaf(); + var leaf = current.AsLeaf(); if (leaf.Header.Count == 0) { key = default!; @@ -750,15 +738,15 @@ namespace PersistentOrderedMap } int last = leaf.Header.Count - 1; - key = leaf.Keys[last]; + 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 + public static bool TryGetSuccessor(Node root, TK key, TStrategy strategy, out TK nextKey, out TV nextValue) + where TStrategy : IKeyStrategy { - InternalNode[] path = new InternalNode[32]; + InternalNode[] path = new InternalNode[32]; int[] indices = new int[32]; int depth = 0; long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; @@ -774,14 +762,14 @@ namespace PersistentOrderedMap current = internalNode.Children[idx]!; } - var leaf = current.AsLeaf(); + 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 && strategy.Compare(leaf.Keys![index], key) == 0) index++; if (index < leaf.Header.Count) { - nextKey = leaf.Keys[index]; + nextKey = leaf.Keys![index]; nextValue = leaf.Values[index]; return true; } @@ -796,8 +784,8 @@ namespace PersistentOrderedMap current = current.AsInternal().Children[0]!; } - var targetLeaf = current.AsLeaf(); - nextKey = targetLeaf.Keys[0]; + var targetLeaf = current.AsLeaf(); + nextKey = targetLeaf.Keys![0]; nextValue = targetLeaf.Values[0]; return true; } @@ -808,10 +796,10 @@ namespace PersistentOrderedMap return false; } - public static bool TryGetPredecessor(Node root, K key, TStrategy strategy, out K prevKey, out V prevValue) - where TStrategy : IKeyStrategy + public static bool TryGetPredecessor(Node root, TK key, TStrategy strategy, out TK prevKey, out TV prevValue) + where TStrategy : IKeyStrategy { - InternalNode[] path = new InternalNode[32]; + InternalNode[] path = new InternalNode[32]; int[] indices = new int[32]; int depth = 0; long keyPrefix = strategy.UsesPrefixes ? strategy.GetPrefix(key) : 0; @@ -827,12 +815,12 @@ namespace PersistentOrderedMap current = internalNode.Children[idx]!; } - var leaf = current.AsLeaf(); + var leaf = current.AsLeaf(); int index = FindIndex(leaf, key, keyPrefix, strategy); if (index > 0) { - prevKey = leaf.Keys[index - 1]; + prevKey = leaf.Keys![index - 1]; prevValue = leaf.Values[index - 1]; return true; } @@ -848,9 +836,9 @@ namespace PersistentOrderedMap current = internalNode.Children[internalNode.Header.Count]!; } - var targetLeaf = current.AsLeaf(); + var targetLeaf = current.AsLeaf(); int last = targetLeaf.Header.Count - 1; - prevKey = targetLeaf.Keys[last]; + prevKey = targetLeaf.Keys![last]; prevValue = targetLeaf.Values[last]; return true; } diff --git a/PersistentOrderedMap/BaseOrderedMap.cs b/PersistentOrderedMap/BaseOrderedMap.cs index c3838eb..393e2ee 100644 --- a/PersistentOrderedMap/BaseOrderedMap.cs +++ b/PersistentOrderedMap/BaseOrderedMap.cs @@ -2,17 +2,17 @@ using System.Collections; namespace PersistentOrderedMap; -public abstract class BaseOrderedMap : IEnumerable> where TStrategy : IKeyStrategy +public abstract class BaseOrderedMap : IEnumerable> where TStrategy : IKeyStrategy { - internal Node _root; - internal readonly TStrategy _strategy; + internal Node Root; + internal readonly TStrategy Strategy; public int Count { get; protected set; } - protected BaseOrderedMap(Node root, TStrategy strategy, int count) + protected BaseOrderedMap(Node root, TStrategy strategy, int count) { - _root = root ?? throw new ArgumentNullException(nameof(root)); - _strategy = strategy ?? throw new ArgumentNullException(nameof(strategy)); + Root = root ?? throw new ArgumentNullException(nameof(root)); + Strategy = strategy ?? throw new ArgumentNullException(nameof(strategy)); Count = count; } @@ -21,14 +21,14 @@ public abstract class BaseOrderedMap : IEnumerable(_root, key, _strategy, out _); + return BTreeFunctions.TryGetValue(Root, key, Strategy, out _); } @@ -37,26 +37,26 @@ public abstract class BaseOrderedMap : IEnumerable Create(TStrategy strategy) + public static PersistentOrderedMap Create(TStrategy strategy) { // Start with an empty leaf owned by None so the first write triggers CoW. - var emptyRoot = new LeafNode(OwnerId.None, strategy.UsesPrefixes); - return new PersistentOrderedMap(emptyRoot, strategy, 0); + var emptyRoot = new LeafNode(OwnerId.None, strategy.UsesPrefixes); + return new PersistentOrderedMap(emptyRoot, strategy, 0); } - public static TransientOrderedMap CreateTransient(TStrategy strategy) + public static TransientOrderedMap CreateTransient(TStrategy strategy) { - var emptyRoot = new LeafNode(OwnerId.None, strategy.UsesPrefixes); - return new TransientOrderedMap(emptyRoot, strategy,0); + var emptyRoot = new LeafNode(OwnerId.None, strategy.UsesPrefixes); + return new TransientOrderedMap(emptyRoot, strategy,0); } - public BTreeEnumerator GetEnumerator() + public BTreeEnumerator GetEnumerator() { return AsEnumerable().GetEnumerator(); } - IEnumerator> IEnumerable>.GetEnumerator() + IEnumerator> IEnumerable>.GetEnumerator() { return GetEnumerator(); } @@ -67,19 +67,19 @@ public abstract class BaseOrderedMap : IEnumerable AsEnumerable() - => new(_root, _strategy, false, default, false, default); + public BTreeEnumerable AsEnumerable() + => new(Root, Strategy, false, default!, false, default!); // 2. Exact Range - public BTreeEnumerable Range(K min, K max) - => new(_root, _strategy, true, min, true, max); + public BTreeEnumerable Range(TK min, TK max) + => new(Root, Strategy, true, min, true, max); // 3. Start From (Open Ended) - public BTreeEnumerable From(K min) => new(_root, _strategy, true, min, false, default); + public BTreeEnumerable From(TK min) => new(Root, Strategy, true, min, false, default!); // 4. Until (Start at beginning) - public BTreeEnumerable Until(K max) - => new(_root, _strategy, false, default, true, max); + public BTreeEnumerable Until(TK max) + => new(Root, Strategy, false, default!, true, max); @@ -87,19 +87,19 @@ public abstract class BaseOrderedMap : IEnumerable BTreeFunctions.TryGetMin(_root, out key, out value); +public bool TryGetMin(out TK key, out TV value) => BTreeFunctions.TryGetMin(Root, out key, out value); -public bool TryGetMax(out K key, out V value) => BTreeFunctions.TryGetMax(_root, out key, out value); +public bool TryGetMax(out TK key, out TV value) => BTreeFunctions.TryGetMax(Root, out key, out value); -public bool TryGetSuccessor(K key, out K nextKey, out V nextValue) => BTreeFunctions.TryGetSuccessor(_root, key, _strategy, out nextKey, out nextValue); +public bool TryGetSuccessor(TK key, out TK nextKey, out TV nextValue) => BTreeFunctions.TryGetSuccessor(Root, key, Strategy, out nextKey, out nextValue); -public bool TryGetPredecessor(K key, out K prevKey, out V prevValue) => BTreeFunctions.TryGetPredecessor(_root, key, _strategy, out prevKey, out prevValue); +public bool TryGetPredecessor(TK key, out TK prevKey, out TV prevValue) => BTreeFunctions.TryGetPredecessor(Root, key, Strategy, out prevKey, out prevValue); // --------------------------------------------------------- // Set Operations (Linear Merge O(N+M)) // --------------------------------------------------------- -public IEnumerable> Intersect(BaseOrderedMap other) +public IEnumerable> Intersect(BaseOrderedMap other) { using var enum1 = this.GetEnumerator(); using var enum2 = other.GetEnumerator(); @@ -109,7 +109,7 @@ public IEnumerable> Intersect(BaseOrderedMap while (has1 && has2) { - int cmp = _strategy.Compare(enum1.Current.Key, enum2.Current.Key); + int cmp = Strategy.Compare(enum1.Current.Key, enum2.Current.Key); if (cmp == 0) { yield return enum1.Current; @@ -121,7 +121,7 @@ public IEnumerable> Intersect(BaseOrderedMap } } -public IEnumerable> Except(BaseOrderedMap other) +public IEnumerable> Except(BaseOrderedMap other) { using var enum1 = this.GetEnumerator(); using var enum2 = other.GetEnumerator(); @@ -131,7 +131,7 @@ public IEnumerable> Except(BaseOrderedMap ot while (has1 && has2) { - int cmp = _strategy.Compare(enum1.Current.Key, enum2.Current.Key); + int cmp = Strategy.Compare(enum1.Current.Key, enum2.Current.Key); if (cmp == 0) { has1 = enum1.MoveNext(); @@ -155,7 +155,7 @@ public IEnumerable> Except(BaseOrderedMap ot } } -public IEnumerable> SymmetricExcept(BaseOrderedMap other) +public IEnumerable> SymmetricExcept(BaseOrderedMap other) { using var enum1 = this.GetEnumerator(); using var enum2 = other.GetEnumerator(); @@ -165,7 +165,7 @@ public IEnumerable> SymmetricExcept(BaseOrderedMap : IEnumerable> -where TStrategy : IKeyStrategy +public struct BTreeEnumerable : IEnumerable> +where TStrategy : IKeyStrategy { - private readonly Node _root; + private readonly Node _root; private readonly TStrategy _strategy; - private readonly K _min, _max; + private readonly TK _min, _max; private readonly bool _hasMin, _hasMax; - public BTreeEnumerable(Node root, TStrategy strategy, bool hasMin, K min, bool hasMax, K max) + public BTreeEnumerable(Node root, TStrategy strategy, bool hasMin, TK min, bool hasMax, TK max) { _root = root; _strategy = strategy; _hasMin = hasMin; _min = min; _hasMax = hasMax; _max = max; } - public BTreeEnumerator GetEnumerator() + public BTreeEnumerator GetEnumerator() { - return new BTreeEnumerator(_root, _strategy, _hasMin, _min, _hasMax, _max); + return new BTreeEnumerator(_root, _strategy, _hasMin, _min, _hasMax, _max); } - IEnumerator> IEnumerable>.GetEnumerator() => GetEnumerator(); + IEnumerator> IEnumerable>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } @@ -35,52 +35,52 @@ where TStrategy : IKeyStrategy // all int-indexed data structures (or bit partitioned ones). // This should be enough for anyone. [InlineArray(16)] -internal struct IterNodeBuffer +internal struct IterNodeBuffer { - private Node _element0; + private Node _element0; } [InlineArray(16)] -internal struct IterIndexBuffer +internal struct IterIndexBuffer { private int _element0; } -public struct BTreeEnumerator : IEnumerator> - where TStrategy : IKeyStrategy +public struct BTreeEnumerator : IEnumerator> + where TStrategy : IKeyStrategy { private readonly TStrategy _strategy; - private readonly Node _root; + private readonly Node _root; // --- BOUNDS --- private readonly bool _hasMax; - private readonly K _maxKey; + private readonly TK _maxKey; private readonly bool _hasMin; - private readonly K _minKey; + private readonly TK _minKey; // --- INLINE STACK --- - private IterNodeBuffer _nodeStack; - private IterIndexBuffer _indexStack; + private IterNodeBuffer _nodeStack; + private IterIndexBuffer _indexStack; private int _depth; // --- STATE --- - private LeafNode? _currentLeaf; + private LeafNode? _currentLeaf; private int _currentLeafIndex; - private KeyValuePair _current; + private KeyValuePair _current; // Unified Constructor // We use boolean flags because 'K' might be a struct where 'null' is impossible. - public BTreeEnumerator(Node root, TStrategy strategy, bool hasMin, K minKey, bool hasMax, K maxKey) + public BTreeEnumerator(Node? root, TStrategy strategy, bool hasMin, TK minKey, bool hasMax, TK maxKey) { - _root = root; + _root = root!; _strategy = strategy; _hasMax = hasMax; _maxKey = maxKey; _hasMin = hasMin; _minKey = minKey; - _nodeStack = new IterNodeBuffer(); - _indexStack = new IterIndexBuffer(); // Explicit struct init + _nodeStack = new IterNodeBuffer(); + _indexStack = new IterIndexBuffer(); // Explicit struct init _depth = 0; _currentLeaf = null; _currentLeafIndex = -1; @@ -102,7 +102,7 @@ public struct BTreeEnumerator : IEnumerator> // Logic 1: Unbounded Start (Go to very first item) private void DiveLeft() { - Node node = _root; + Node node = _root; _depth = 0; while (!node.IsLeaf) @@ -114,14 +114,14 @@ public struct BTreeEnumerator : IEnumerator> node = internalNode.Children[0]!; } - _currentLeaf = node.AsLeaf(); + _currentLeaf = node.AsLeaf(); _currentLeafIndex = -1; // Position before the first element (0) } // Logic 2: Bounded Start (Go to specific key) - private void Seek(K key) + private void Seek(TK key) { - Node node = _root; + Node node = _root; _depth = 0; long keyPrefix = _strategy.UsesPrefixes ? _strategy.GetPrefix(key) : 0; @@ -130,7 +130,7 @@ public struct BTreeEnumerator : IEnumerator> while (!node.IsLeaf) { var internalNode = node.AsInternal(); - int idx = BTreeFunctions.FindRoutingIndex(internalNode, key, keyPrefix, _strategy); + int idx = BTreeFunctions.FindRoutingIndex(internalNode, key, keyPrefix, _strategy); _nodeStack[_depth] = internalNode; _indexStack[_depth] = idx; @@ -140,8 +140,8 @@ public struct BTreeEnumerator : IEnumerator> } // Find index in Leaf - _currentLeaf = node.AsLeaf(); - int index = BTreeFunctions.FindIndex(_currentLeaf, key, keyPrefix, _strategy); + _currentLeaf = node.AsLeaf(); + int index = BTreeFunctions.FindIndex(_currentLeaf, key, keyPrefix, _strategy); // Set position to (index - 1) so that the first MoveNext() lands on 'index' _currentLeafIndex = index - 1; @@ -158,14 +158,14 @@ public struct BTreeEnumerator : IEnumerator> if (_hasMax) { // If Current Key > Max Key, we are done. - if (_strategy.Compare(_currentLeaf.Keys[_currentLeafIndex], _maxKey) > 0) + if (_strategy.Compare(_currentLeaf.Keys![_currentLeafIndex], _maxKey) > 0) { _currentLeaf = null; // Close iterator return false; } } - _current = new KeyValuePair(_currentLeaf.Keys[_currentLeafIndex], _currentLeaf.Values[_currentLeafIndex]); + _current = new KeyValuePair(_currentLeaf.Keys![_currentLeafIndex], _currentLeaf.Values[_currentLeafIndex]); return true; } @@ -176,14 +176,14 @@ public struct BTreeEnumerator : IEnumerator> // Check Max Bound immediately for the first item if (_hasMax) { - if (_strategy.Compare(_currentLeaf!.Keys[0], _maxKey) > 0) + if (_strategy.Compare(_currentLeaf.Keys![0], _maxKey) > 0) { _currentLeaf = null; return false; } } - _current = new KeyValuePair(_currentLeaf.Keys[0], _currentLeaf.Values[0]); + _current = new KeyValuePair(_currentLeaf.Keys![0], _currentLeaf.Values[0]); return true; } @@ -204,7 +204,7 @@ public struct BTreeEnumerator : IEnumerator> _indexStack[_depth] = nextIndex; _depth++; - Node node = internalNode.Children[nextIndex]!; + Node node = internalNode.Children[nextIndex]!; while (!node.IsLeaf) { _nodeStack[_depth] = node; @@ -213,7 +213,7 @@ public struct BTreeEnumerator : IEnumerator> node = node.AsInternal().Children[0]!; } - _currentLeaf = node.AsLeaf(); + _currentLeaf = node.AsLeaf(); _currentLeafIndex = 0; return true; } @@ -221,7 +221,7 @@ public struct BTreeEnumerator : IEnumerator> return false; } - public KeyValuePair Current => _current; + public KeyValuePair Current => _current; object IEnumerator.Current => _current; public void Reset() { diff --git a/PersistentOrderedMap/KeyStrategies.cs b/PersistentOrderedMap/KeyStrategies.cs index f960d1d..9ef540d 100644 --- a/PersistentOrderedMap/KeyStrategies.cs +++ b/PersistentOrderedMap/KeyStrategies.cs @@ -1,7 +1,3 @@ -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; namespace PersistentOrderedMap; @@ -9,10 +5,10 @@ using System; using System.Buffers.Binary; using System.Runtime.CompilerServices; -public interface IKeyStrategy +public interface IKeyStrategy { - int Compare(K x, K y); - long GetPrefix(K key); + int Compare(TK x, TK y); + long GetPrefix(TK key); bool UsesPrefixes => true; diff --git a/PersistentOrderedMap/KeyStrategies/ComparableStrategy.cs b/PersistentOrderedMap/KeyStrategies/ComparableStrategy.cs index a9aa726..c41acab 100644 --- a/PersistentOrderedMap/KeyStrategies/ComparableStrategy.cs +++ b/PersistentOrderedMap/KeyStrategies/ComparableStrategy.cs @@ -4,14 +4,14 @@ using System.Runtime.CompilerServices; // This is a comparable strategy that may squeeze some extra time out of value types -public readonly struct ComparableStrategy : IKeyStrategy where K : IComparable +public readonly struct ComparableStrategy : IKeyStrategy where TK : IComparable { public bool UsesPrefixes => false; public bool UseBinarySearch => true; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long GetPrefix(K key) => 0; + public long GetPrefix(TK key) => 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Compare(K x, K y) => x.CompareTo(y); + public int Compare(TK x, TK y) => x.CompareTo(y); } diff --git a/PersistentOrderedMap/KeyStrategies/StandardStrategy.cs b/PersistentOrderedMap/KeyStrategies/StandardStrategy.cs index aefca25..996c808 100644 --- a/PersistentOrderedMap/KeyStrategies/StandardStrategy.cs +++ b/PersistentOrderedMap/KeyStrategies/StandardStrategy.cs @@ -5,21 +5,21 @@ using System.Runtime.CompilerServices; /// A universal key strategy for any type that relies on standard comparisons /// (IComparable, IComparer, or custom StringComparers) without SIMD prefixes. /// -public readonly struct StandardStrategy : IKeyStrategy +public readonly struct StandardStrategy : IKeyStrategy { - private readonly IComparer _comparer; + private readonly IComparer _comparer; // If no comparer is provided, it defaults to Comparer.Default // which automatically uses IComparable if the type implements it. public StandardStrategy() { - _comparer = Comparer.Default; + _comparer = Comparer.Default; } - public StandardStrategy(IComparer? comparer) + public StandardStrategy(IComparer? comparer) { - _comparer = comparer ?? Comparer.Default; + _comparer = comparer ?? Comparer.Default; } // Tell the B-Tree to skip SIMD routing and just use LinearSearch public bool UsesPrefixes => false; @@ -27,16 +27,16 @@ public readonly struct StandardStrategy : IKeyStrategy // This will never be called because UsesPrefixes is false, // but we must satisfy the interface. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long GetPrefix(K key) => 0; + public long GetPrefix(TK key) => 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Compare(K x, K y) + public int Compare(TK x, TK y) { return _comparer.Compare(x, y); } } -public readonly struct StandardStrategy2 : IKeyStrategy - where TComparer : struct, IComparer +public readonly struct StandardStrategy2 : IKeyStrategy + where TComparer : struct, IComparer { private readonly TComparer _comparer; @@ -46,8 +46,8 @@ public readonly struct StandardStrategy2 : IKeyStrategy public bool UseBinarySearch => true; [MethodImpl(MethodImplOptions.AggressiveInlining)] -public int Compare(K x, K y) => _comparer.Compare(x, y); +public int Compare(TK x, TK y) => _comparer.Compare(x, y); - public long GetPrefix(K key) => 0; + public long GetPrefix(TK key) => 0; } diff --git a/PersistentOrderedMap/Nodes.cs b/PersistentOrderedMap/Nodes.cs index 8e1cc76..20499ca 100644 --- a/PersistentOrderedMap/Nodes.cs +++ b/PersistentOrderedMap/Nodes.cs @@ -33,17 +33,17 @@ public struct NodeHeader } [InlineArray(32)] -public struct KeyBuffer +public struct KeyBuffer { - private K _element0; + private TK _element0; } // Constraint: Internal Nodes fixed at 32 children. // This removes the need for a separate array allocation for children references. [InlineArray(32)] -public struct NodeBuffer +public struct NodeBuffer { - private Node? _element0; + private Node? _element0; } [InlineArray(32)] @@ -52,7 +52,7 @@ internal struct InternalPrefixBuffer private long _element0; } -public abstract class Node +public abstract class Node { public NodeHeader Header; @@ -61,7 +61,7 @@ public abstract class Node Header = new NodeHeader(owner, 0, flags); } - public abstract Span GetKeys(); + public abstract Span GetKeys(); // Abstract access to prefixes regardless of storage backing public abstract Span AllPrefixes { get; } @@ -71,47 +71,47 @@ public abstract class Node public bool IsLeaf => (Header.Flags & NodeFlags.IsLeaf) != 0; - public abstract Node EnsureEditable(OwnerId transactionId); + public abstract Node EnsureEditable(OwnerId transactionId); public void SetCount(int newCount) => Header.Count = (byte)newCount; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public LeafNode AsLeaf() + public LeafNode AsLeaf() { // Zero-overhead cast. Assumes you checked IsLeaf or know logic flow. - return Unsafe.As>(this); + return Unsafe.As>(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public InternalNode AsInternal() + public InternalNode AsInternal() { // Zero-overhead cast. Assumes you checked !IsLeaf or know logic flow. - return Unsafe.As>(this); + return Unsafe.As>(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PrefixInternalNode AsPrefixInternal() + public PrefixInternalNode AsPrefixInternal() { - return Unsafe.As>(this); + return Unsafe.As>(this); } } -public sealed class LeafNode : Node +public sealed class LeafNode : Node { public const int Capacity = 64; public const int MergeThreshold = 8; - public K[]? Keys; - public V[] Values; + public TK[]? Keys; + public TV[] Values; - internal long[]? _prefixes; + private long[]? _prefixes; public override Span AllPrefixes => _prefixes != null ? _prefixes : Span.Empty; public LeafNode(OwnerId owner, bool usePrefixes) : base(owner, NodeFlags.IsLeaf | (usePrefixes ? NodeFlags.HasPrefixes : NodeFlags.None)) { - Keys = new K[Capacity]; - Values = new V[Capacity]; + Keys = new TK[Capacity]; + Values = new TV[Capacity]; if (usePrefixes) { _prefixes = new long[Capacity]; @@ -119,21 +119,21 @@ public sealed class LeafNode : Node } // Copy Constructor for CoW - private LeafNode(LeafNode original, OwnerId newOwner) + private LeafNode(LeafNode original, OwnerId newOwner) : base(newOwner, original.Header.Flags) { - Keys = new K[Capacity]; - Values = new V[Capacity]; + Keys = new TK[Capacity]; + Values = new TV[Capacity]; Header.Count = original.Header.Count; _prefixes = new long[Capacity]; // Copy data - Array.Copy(original.Keys, Keys, original.Header.Count); + Array.Copy(original.Keys!, Keys, original.Header.Count); Array.Copy(original.Values, Values, original.Header.Count); if (original._prefixes != null) Array.Copy(original._prefixes, _prefixes, original.Header.Count); } - public override Node EnsureEditable(OwnerId transactionId) + public override Node EnsureEditable(OwnerId transactionId) { // CASE 1: Persistent Mode (transactionId is None). // We MUST create a copy, because we cannot distinguish "Shared Immutable Node (0)" @@ -142,7 +142,7 @@ public sealed class LeafNode : Node // we won't copy the same fresh node twice. if (transactionId == OwnerId.None) { - return new LeafNode(this, OwnerId.None); + return new LeafNode(this, OwnerId.None); } // CASE 2: Transient Mode. @@ -153,26 +153,26 @@ public sealed class LeafNode : Node } // CASE 3: CoW needed (Ownership mismatch). - return new LeafNode(this, transactionId); + return new LeafNode(this, transactionId); } - public override Span GetKeys() + public override Span GetKeys() { return Keys.AsSpan(0, Header.Count); } - public Span GetValues() + public Span GetValues() { return Values.AsSpan(0, Header.Count); } } -public class InternalNode : Node +public class InternalNode : Node { public const int Capacity = 32; - public KeyBuffer Keys; - public NodeBuffer Children; + public KeyBuffer Keys; + public NodeBuffer Children; public override Span AllPrefixes => Span.Empty; @@ -182,7 +182,7 @@ public class InternalNode : Node } // Fixed CoW Constructor - protected InternalNode(InternalNode original, OwnerId newOwner, NodeFlags flags) + protected InternalNode(InternalNode original, OwnerId newOwner, NodeFlags flags) : base(newOwner, flags) { Header.Count = original.Header.Count; @@ -195,28 +195,28 @@ public class InternalNode : Node // The missing method needed by BTreeFunctions for routing [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span> GetChildren() + public Span> GetChildren() { // An internal node always has (Count + 1) children - return MemoryMarshal.CreateSpan(ref Children[0], Header.Count + 1); + return MemoryMarshal.CreateSpan(ref Children[0]!, Header.Count + 1); } - public override Span GetKeys() => MemoryMarshal.CreateSpan(ref Keys[0], Header.Count); + public override Span GetKeys() => MemoryMarshal.CreateSpan(ref Keys[0], Header.Count); - public override Node EnsureEditable(OwnerId transactionId) + public override Node EnsureEditable(OwnerId transactionId) { - if (transactionId == OwnerId.None) return new InternalNode(this, OwnerId.None, Header.Flags); + if (transactionId == OwnerId.None) return new InternalNode(this, OwnerId.None, Header.Flags); if (Header.Owner == transactionId) return this; - return new InternalNode(this, transactionId, Header.Flags); + return new InternalNode(this, transactionId, Header.Flags); } } -public sealed class PrefixInternalNode : InternalNode +public sealed class PrefixInternalNode : InternalNode { - internal InternalPrefixBuffer _prefixBuffer; + internal InternalPrefixBuffer PrefixBuffer; - public override Span AllPrefixes => MemoryMarshal.CreateSpan(ref _prefixBuffer[0], Capacity); + public override Span AllPrefixes => MemoryMarshal.CreateSpan(ref PrefixBuffer[0], Capacity); public PrefixInternalNode(OwnerId owner) : base(owner, NodeFlags.HasPrefixes) @@ -224,18 +224,18 @@ public sealed class PrefixInternalNode : InternalNode } // CoW Constructor - private PrefixInternalNode(PrefixInternalNode original, OwnerId newOwner) + private PrefixInternalNode(PrefixInternalNode original, OwnerId newOwner) : base(original, newOwner, original.Header.Flags) { // Copy the base Keys and Children, then blit the prefix buffer - this._prefixBuffer = original._prefixBuffer; + this.PrefixBuffer = original.PrefixBuffer; } - public override Node EnsureEditable(OwnerId transactionId) + public override Node EnsureEditable(OwnerId transactionId) { - if (transactionId == OwnerId.None) return new PrefixInternalNode(this, OwnerId.None); + if (transactionId == OwnerId.None) return new PrefixInternalNode(this, OwnerId.None); if (Header.Owner == transactionId) return this; - return new PrefixInternalNode(this, transactionId); + return new PrefixInternalNode(this, transactionId); } } diff --git a/PersistentOrderedMap/PersistentOrderedMap.cs b/PersistentOrderedMap/PersistentOrderedMap.cs index 5d45114..9731141 100644 --- a/PersistentOrderedMap/PersistentOrderedMap.cs +++ b/PersistentOrderedMap/PersistentOrderedMap.cs @@ -1,44 +1,43 @@ -using System.Collections; namespace PersistentOrderedMap; -public sealed class PersistentOrderedMap : BaseOrderedMap, IEnumerable, IEnumerable> where TStrategy : IKeyStrategy +public sealed class PersistentOrderedMap : BaseOrderedMap where TStrategy : IKeyStrategy { - internal PersistentOrderedMap(Node root, TStrategy strategy, int count) + internal PersistentOrderedMap(Node root, TStrategy strategy, int count) : base(root, strategy, count) { } // --------------------------------------------------------- // Immutable Write API (Returns new Map) // --------------------------------------------------------- - public PersistentOrderedMap Set(K key, V value) + public PersistentOrderedMap Set(TK key, TV value) { // OPTIMIZATION: Use OwnerId.None (0). // This signals EnsureEditable to always copy the root path, // producing a new tree of nodes that also have OwnerId.None. - var newRoot = BTreeFunctions.Set(_root, key, value, _strategy, OwnerId.None, out bool countChanged); - return new PersistentOrderedMap(newRoot, _strategy, countChanged ? Count + 1 : Count); + var newRoot = BTreeFunctions.Set(Root, key, value, Strategy, OwnerId.None, out bool countChanged); + return new PersistentOrderedMap(newRoot, Strategy, countChanged ? Count + 1 : Count); } - public static PersistentOrderedMap Empty(TStrategy strategy) + public static PersistentOrderedMap Empty(TStrategy strategy) { // Create an empty Leaf Node. // 'default(OwnerId)' (usually 0) marks this node as Immutable/Persistent. // This ensures that any subsequent Set/Remove will clone this node // instead of modifying it in place. - var emptyRoot = new LeafNode(default(OwnerId), strategy.UsesPrefixes); + var emptyRoot = new LeafNode(default(OwnerId), strategy.UsesPrefixes); - return new PersistentOrderedMap(emptyRoot, strategy, 0); + return new PersistentOrderedMap(emptyRoot, strategy, 0); } - public PersistentOrderedMap Remove(K key) + public PersistentOrderedMap Remove(TK key) { - var newRoot = BTreeFunctions.Remove(_root, key, _strategy, OwnerId.None, out bool removed); + var newRoot = BTreeFunctions.Remove(Root, key, Strategy, OwnerId.None, out bool removed); if (!removed) return this; - return new PersistentOrderedMap(newRoot, _strategy, Count - 1); + return new PersistentOrderedMap(newRoot, Strategy, Count - 1); } - public TransientOrderedMap ToTransient() + public TransientOrderedMap ToTransient() { - return new TransientOrderedMap(_root, _strategy, Count); + return new TransientOrderedMap(Root, Strategy, Count); } } diff --git a/PersistentOrderedMap/TransientOrderedMap.cs b/PersistentOrderedMap/TransientOrderedMap.cs index 48d4248..93786f8 100644 --- a/PersistentOrderedMap/TransientOrderedMap.cs +++ b/PersistentOrderedMap/TransientOrderedMap.cs @@ -1,35 +1,34 @@ -using System.Collections; namespace PersistentOrderedMap; -public sealed class TransientOrderedMap : BaseOrderedMap where TStrategy : IKeyStrategy +public sealed class TransientOrderedMap : BaseOrderedMap where TStrategy : IKeyStrategy { // This is mutable, but we treat it as readonly for the ID generation logic usually. private OwnerId _transactionId; - public TransientOrderedMap(Node root, TStrategy strategy, int count) + public TransientOrderedMap(Node root, TStrategy strategy, int count) : base(root, strategy, count) { _transactionId = OwnerId.Next(); } - public void Set(K key, V value) + public void Set(TK key, TV value) { - _root = BTreeFunctions.Set(_root, key, value, _strategy, _transactionId, out bool countChanged); + Root = BTreeFunctions.Set(Root, key, value, Strategy, _transactionId, out bool countChanged); if (countChanged) Count++; } - public void Remove(K key) + public void Remove(TK key) { - _root = BTreeFunctions.Remove(_root, key, _strategy, _transactionId, out bool removed); + Root = BTreeFunctions.Remove(Root, key, Strategy, _transactionId, out bool removed); if (removed) Count--; } - public PersistentOrderedMap ToPersistent() + public PersistentOrderedMap ToPersistent() { // 1. Create the snapshot by copying all relevant information - var snapshot = new PersistentOrderedMap(_root, _strategy, Count); + var snapshot = new PersistentOrderedMap(Root, Strategy, Count); // 2. Protect the snapshot from THIS TransientOrderedMap by getting a new ownerId // so that future edits will be done by CoW diff --git a/TestProject1/FuzzTest.cs b/TestProject1/FuzzTest.cs index 643db48..bc4a081 100644 --- a/TestProject1/FuzzTest.cs +++ b/TestProject1/FuzzTest.cs @@ -19,25 +19,25 @@ public class BTreeFuzzTests public void Fuzz_Insert_And_Remove_consistency() { // CONFIGURATION - const int Iterations = 100_000; // High enough to trigger all splits/merges - const int KeyRange = 5000; // Small enough to cause frequent collisions + const int iterations = 100_000; // High enough to trigger all splits/merges + const int keyRange = 5000; // Small enough to cause frequent collisions const bool showOps = false; - int Seed = 2135974; // Environment.TickCount; + int seed = 2135974; // Environment.TickCount; // ORACLES var reference = new SortedDictionary(); var subject = BaseOrderedMap.CreateTransient(_strategy); - var random = new Random(Seed); - _output.WriteLine($"Starting Fuzz Test with Seed: {Seed}"); + var random = new Random(seed); + _output.WriteLine($"Starting Fuzz Test with Seed: {seed}"); try { - for (int i = 0; i < Iterations; i++) + for (int i = 0; i < iterations; i++) { // 1. Pick an Action: 70% Insert/Update, 30% Remove bool isInsert = random.NextDouble() < 0.7; - int key = random.Next(KeyRange); + int key = random.Next(keyRange); int val = key * 100; if (isInsert) @@ -82,7 +82,7 @@ public class BTreeFuzzTests } catch (Exception) { - _output.WriteLine($"FAILED at iteration with SEED: {Seed}"); + _output.WriteLine($"FAILED at iteration with SEED: {seed}"); throw; // Re-throw to fail the test } } @@ -91,28 +91,28 @@ public class BTreeFuzzTests public void Fuzz_Range_Queries() { // Validates that your Range Enumerator matches LINQ on the reference - const int Iterations = 1000; - const int KeyRange = 2000; - int Seed = Environment.TickCount; + const int iterations = 1000; + const int keyRange = 2000; + int seed = Environment.TickCount; var reference = new SortedDictionary(); var subject = BaseOrderedMap.CreateTransient(_strategy); - var random = new Random(Seed); + var random = new Random(seed); // Fill Data - for(int i=0; i= min + int min = random.Next(keyRange); + int max = min + random.Next(keyRange - min); // Ensure max >= min // 1. Reference Result (LINQ) // Note: SortedDictionary doesn't have a direct Range query, so we filter memory. diff --git a/TestProject1/FuzzTestStandardStrategy.cs b/TestProject1/FuzzTestStandardStrategy.cs index 958498a..5c5ee61 100644 --- a/TestProject1/FuzzTestStandardStrategy.cs +++ b/TestProject1/FuzzTestStandardStrategy.cs @@ -19,25 +19,25 @@ public class BTreeFuzzTestStandardStrategy public void Fuzz_Insert_And_Remove_consistency() { // CONFIGURATION - const int Iterations = 100_000; // High enough to trigger all splits/merges - const int KeyRange = 5000; // Small enough to cause frequent collisions + const int iterations = 100_000; // High enough to trigger all splits/merges + const int keyRange = 5000; // Small enough to cause frequent collisions const bool showOps = false; - int Seed = 2135974; // Environment.TickCount; + int seed = 2135974; // Environment.TickCount; // ORACLES var reference = new SortedDictionary(); var subject = BaseOrderedMap>.CreateTransient(_strategy); - var random = new Random(Seed); - _output.WriteLine($"Starting Fuzz Test with Seed: {Seed}"); + var random = new Random(seed); + _output.WriteLine($"Starting Fuzz Test with Seed: {seed}"); try { - for (int i = 0; i < Iterations; i++) + for (int i = 0; i < iterations; i++) { // 1. Pick an Action: 70% Insert/Update, 30% Remove bool isInsert = random.NextDouble() < 0.7; - int key = random.Next(KeyRange); + int key = random.Next(keyRange); int val = key * 100; if (isInsert) @@ -82,7 +82,7 @@ public class BTreeFuzzTestStandardStrategy } catch (Exception) { - _output.WriteLine($"FAILED at iteration with SEED: {Seed}"); + _output.WriteLine($"FAILED at iteration with SEED: {seed}"); throw; // Re-throw to fail the test } } @@ -91,28 +91,28 @@ public class BTreeFuzzTestStandardStrategy public void Fuzz_Range_Queries() { // Validates that your Range Enumerator matches LINQ on the reference - const int Iterations = 1000; - const int KeyRange = 2000; - int Seed = Environment.TickCount; + const int iterations = 1000; + const int keyRange = 2000; + int seed = Environment.TickCount; var reference = new SortedDictionary(); var subject = BaseOrderedMap>.CreateTransient(_strategy); - var random = new Random(Seed); + var random = new Random(seed); // Fill Data - for(int i=0; i= min + int min = random.Next(keyRange); + int max = min + random.Next(keyRange - min); // Ensure max >= min // 1. Reference Result (LINQ) // Note: SortedDictionary doesn't have a direct Range query, so we filter memory. diff --git a/TestProject1/StandardStrategy.cs b/TestProject1/StandardStrategy.cs index b5467b0..94cc484 100644 --- a/TestProject1/StandardStrategy.cs +++ b/TestProject1/StandardStrategy.cs @@ -13,26 +13,26 @@ public class StandardStrategy [Fact] public void Setup() { - var N = 1000; - var _stdStrategy = new StandardStrategy(); - var _uniStrategy = new UnicodeStrategy(); + var n = 1000; + var stdStrategy = new StandardStrategy(); + var uniStrategy = new UnicodeStrategy(); var rnd = new Random(42); - var StringLength = 10; + var stringLength = 10; // Build random strings - var _allKeys = Enumerable.Range(0, N).Select(_ => GenerateRandomString(StringLength, rnd)).Distinct().ToArray(); + var allKeys = Enumerable.Range(0, n).Select(_ => GenerateRandomString(stringLength, rnd)).Distinct().ToArray(); // Regenerate if Distinct() reduced array size (highly unlikely with length 8/50, but safe) - while (_allKeys.Length < N) + while (allKeys.Length < n) { - _allKeys = _allKeys.Concat(new[] { GenerateRandomString(StringLength, rnd) }).Distinct().ToArray(); + allKeys = allKeys.Concat(new[] { GenerateRandomString(stringLength, rnd) }).Distinct().ToArray(); } - var transStd = BaseOrderedMap>.CreateTransient(_stdStrategy); - var transUni = BaseOrderedMap.CreateTransient(_uniStrategy); - for (int i = 0; i < _allKeys.Length; i++) + var transStd = BaseOrderedMap>.CreateTransient(stdStrategy); + var transUni = BaseOrderedMap.CreateTransient(uniStrategy); + for (int i = 0; i < allKeys.Length; i++) { - transStd.Set(_allKeys[i], i); - transUni.Set(_allKeys[i], i); + transStd.Set(allKeys[i], i); + transUni.Set(allKeys[i], i); } } } \ No newline at end of file