From 92dca5d8b9d8fbb33cd96d81b628cfd691ff3792 Mon Sep 17 00:00:00 2001 From: Szymon Kulec Date: Fri, 26 Jul 2024 14:07:49 +0200 Subject: [PATCH] NibblePath different equality (#377) * shorter comparison * different equals * format * better benchmarks --- .../NibblePathBenchmarks.cs | 11 +++- src/Paprika/Data/NibblePath.cs | 50 +++++++++++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/Paprika.Benchmarks/NibblePathBenchmarks.cs b/src/Paprika.Benchmarks/NibblePathBenchmarks.cs index 7da94a79..4c4e4224 100644 --- a/src/Paprika.Benchmarks/NibblePathBenchmarks.cs +++ b/src/Paprika.Benchmarks/NibblePathBenchmarks.cs @@ -25,7 +25,16 @@ public bool Equals_not_equal_sliced() } [Benchmark(OperationsPerInvoke = 2)] - public bool Equals_equal_sliced() + [Arguments(0, 1)] + [Arguments(1, 1)] + [Arguments(0, 16)] + [Arguments(1, 16)] + [Arguments(0, 32)] + [Arguments(1, 32)] + [Arguments(0, 48)] + [Arguments(1, 48)] + [Arguments(0, NibblePath.KeccakNibbleCount)] + public bool Equals(int slice, int length) { // hammer odd and not var a = NibblePath.FromKey(A1); diff --git a/src/Paprika/Data/NibblePath.cs b/src/Paprika/Data/NibblePath.cs index 0de9a6f5..03f2bbed 100644 --- a/src/Paprika/Data/NibblePath.cs +++ b/src/Paprika/Data/NibblePath.cs @@ -646,10 +646,54 @@ public override string ToString() public bool Equals(in NibblePath other) { - if (other.Length != Length || (other._odd & OddBit) != (_odd & OddBit)) + if (((other.Length ^ Length) | (other._odd ^ _odd)) > 0) return false; - return FindFirstDifferentNibble(other) == Length; + ref var left = ref _span; + ref var right = ref other._span; + var length = Length; + + if (other._odd == OddBit) + { + // This means first byte is not a whole byte + if (((left ^ right) & NibbleMask) > 0) + { + // First nibble differs + return false; + } + + // Move beyond first + left = ref Unsafe.Add(ref left, 1); + right = ref Unsafe.Add(ref right, 1); + + // One nibble already consumed, reduce the length + length -= 1; + } + + if ((length & OddBit) == OddBit) + { + const int highNibbleMask = NibbleMask << NibbleShift; + + // Length is odd, which requires checking the last byte but only the first nibble + if (((Unsafe.Add(ref left, length >> 1) ^ Unsafe.Add(ref right, length >> 1)) + & highNibbleMask) > 0) + { + return false; + } + + // Last nibble already consumed, reduce the length + length -= 1; + } + + if (length == 0) + return true; + + Debug.Assert(length % 2 == 0); + + var leftSpan = MemoryMarshal.CreateReadOnlySpan(ref left, length >> 1); + var rightSpan = MemoryMarshal.CreateReadOnlySpan(ref right, length >> 1); + + return leftSpan.SequenceEqual(rightSpan); } public override int GetHashCode() @@ -760,4 +804,4 @@ static void ThrowNotEnoughMemory() { throw new ArgumentException("Not enough memory to append"); } -} +} \ No newline at end of file