From 759f74f6a4c8b6c028fba4f349ab2d3feefef363 Mon Sep 17 00:00:00 2001 From: Oleksandr Melnyk Date: Thu, 2 Jun 2022 15:43:52 +0300 Subject: [PATCH] Change project structure --- src/Standart.Hash.xxHash.Perf/Program.cs | 2 +- ...feBufferBenchmark.cs => UtilsBenchmark.cs} | 4 +- .../{UnsafeBufferTest.cs => UtilsTest.cs} | 4 +- src/Standart.Hash.xxHash/AssemblyInfo.cs | 4 - src/Standart.Hash.xxHash/BitUtils.cs | 27 -- .../Standart.Hash.xxHash.csproj | 5 + src/Standart.Hash.xxHash/UnsafeBuffer.cs | 235 --------------- src/Standart.Hash.xxHash/Utils.cs | 227 ++++++++++++++ src/Standart.Hash.xxHash/xxHash32.Array.cs | 60 ---- src/Standart.Hash.xxHash/xxHash32.Async.cs | 89 ------ src/Standart.Hash.xxHash/xxHash32.Span.cs | 48 --- src/Standart.Hash.xxHash/xxHash32.Stream.cs | 67 ----- src/Standart.Hash.xxHash/xxHash32.Unsafe.cs | 200 ------------- src/Standart.Hash.xxHash/xxHash32.XXH.cs | 20 ++ src/Standart.Hash.xxHash/xxHash32.XXH32.cs | 176 +++++++++++ src/Standart.Hash.xxHash/xxHash32.cs | 262 +++++++++++++++++ src/Standart.Hash.xxHash/xxHash64.Array.cs | 58 ---- src/Standart.Hash.xxHash/xxHash64.Async.cs | 88 ------ src/Standart.Hash.xxHash/xxHash64.Span.cs | 47 --- src/Standart.Hash.xxHash/xxHash64.Stream.cs | 67 ----- src/Standart.Hash.xxHash/xxHash64.Unsafe.cs | 278 ------------------ src/Standart.Hash.xxHash/xxHash64.XXH.cs | 20 ++ src/Standart.Hash.xxHash/xxHash64.XXH64.cs | 253 ++++++++++++++++ src/Standart.Hash.xxHash/xxHash64.cs | 261 ++++++++++++++++ 24 files changed, 1229 insertions(+), 1273 deletions(-) rename src/Standart.Hash.xxHash.Perf/{UnsafeBufferBenchmark.cs => UtilsBenchmark.cs} (84%) rename src/Standart.Hash.xxHash.Test/{UnsafeBufferTest.cs => UtilsTest.cs} (86%) delete mode 100644 src/Standart.Hash.xxHash/AssemblyInfo.cs delete mode 100644 src/Standart.Hash.xxHash/BitUtils.cs delete mode 100644 src/Standart.Hash.xxHash/UnsafeBuffer.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash32.Array.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash32.Async.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash32.Span.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash32.Stream.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash32.Unsafe.cs create mode 100644 src/Standart.Hash.xxHash/xxHash32.XXH.cs create mode 100644 src/Standart.Hash.xxHash/xxHash32.XXH32.cs create mode 100644 src/Standart.Hash.xxHash/xxHash32.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash64.Array.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash64.Async.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash64.Span.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash64.Stream.cs delete mode 100644 src/Standart.Hash.xxHash/xxHash64.Unsafe.cs create mode 100644 src/Standart.Hash.xxHash/xxHash64.XXH.cs create mode 100644 src/Standart.Hash.xxHash/xxHash64.XXH64.cs create mode 100644 src/Standart.Hash.xxHash/xxHash64.cs diff --git a/src/Standart.Hash.xxHash.Perf/Program.cs b/src/Standart.Hash.xxHash.Perf/Program.cs index 24bdcb0..d1dadc4 100644 --- a/src/Standart.Hash.xxHash.Perf/Program.cs +++ b/src/Standart.Hash.xxHash.Perf/Program.cs @@ -7,7 +7,7 @@ public static void Main(string[] args) { BenchmarkRunner.Run(); - BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } } } \ No newline at end of file diff --git a/src/Standart.Hash.xxHash.Perf/UnsafeBufferBenchmark.cs b/src/Standart.Hash.xxHash.Perf/UtilsBenchmark.cs similarity index 84% rename from src/Standart.Hash.xxHash.Perf/UnsafeBufferBenchmark.cs rename to src/Standart.Hash.xxHash.Perf/UtilsBenchmark.cs index e145ec3..4ccf391 100644 --- a/src/Standart.Hash.xxHash.Perf/UnsafeBufferBenchmark.cs +++ b/src/Standart.Hash.xxHash.Perf/UtilsBenchmark.cs @@ -6,7 +6,7 @@ [RPlotExporter, RankColumn] [MinColumn, MaxColumn] [MemoryDiagnoser] - public class UnsafeBufferBenchmark + public class UtilsBenchmark { private byte[] src; private byte[] des; @@ -33,7 +33,7 @@ [Benchmark] public void UnsafeBufferCopy() { - UnsafeBuffer.BlockCopy(src, 0, des, 0, 32); + Utils.BlockCopy(src, 0, des, 0, 32); } } } \ No newline at end of file diff --git a/src/Standart.Hash.xxHash.Test/UnsafeBufferTest.cs b/src/Standart.Hash.xxHash.Test/UtilsTest.cs similarity index 86% rename from src/Standart.Hash.xxHash.Test/UnsafeBufferTest.cs rename to src/Standart.Hash.xxHash.Test/UtilsTest.cs index 0000b57..8309dd5 100644 --- a/src/Standart.Hash.xxHash.Test/UnsafeBufferTest.cs +++ b/src/Standart.Hash.xxHash.Test/UtilsTest.cs @@ -3,7 +3,7 @@ using System; using Xunit; - public class UnsafeBufferTest + public class UtilsTest { [Fact] public void Copy_different_blocks() @@ -27,7 +27,7 @@ // Act, Assert foreach (int count in counts) { - UnsafeBuffer.BlockCopy(src, 0, dst, 0, count); + Utils.BlockCopy(src, 0, dst, 0, count); for (int i = 0; i < count; i++) { diff --git a/src/Standart.Hash.xxHash/AssemblyInfo.cs b/src/Standart.Hash.xxHash/AssemblyInfo.cs deleted file mode 100644 index 62476bf..0000000 --- a/src/Standart.Hash.xxHash/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Standart.Hash.xxHash.Perf")] -[assembly: InternalsVisibleTo("Standart.Hash.xxHash.Test")] \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/BitUtils.cs b/src/Standart.Hash.xxHash/BitUtils.cs deleted file mode 100644 index 8d5c687..0000000 --- a/src/Standart.Hash.xxHash/BitUtils.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace Standart.Hash.xxHash -{ - internal static class BitUtils - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint RotateLeft(uint value, int offset) - { -#if FCL_BITOPS - return System.Numerics.BitOperations.RotateLeft(value, offset); -#else - return (value << offset) | (value >> (32 - offset)); -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong RotateLeft(ulong value, int offset) // Taken help from: https://stackoverflow.com/a/48580489/5592276 - { -#if FCL_BITOPS - return System.Numerics.BitOperations.RotateLeft(value, offset); -#else - return (value << offset) | (value >> (64 - offset)); -#endif - } - } -} diff --git a/src/Standart.Hash.xxHash/Standart.Hash.xxHash.csproj b/src/Standart.Hash.xxHash/Standart.Hash.xxHash.csproj index 8e38cef..e4ecea4 100644 --- a/src/Standart.Hash.xxHash/Standart.Hash.xxHash.csproj +++ b/src/Standart.Hash.xxHash/Standart.Hash.xxHash.csproj @@ -6,5 +6,10 @@ net6.0; true + + + + + \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/UnsafeBuffer.cs b/src/Standart.Hash.xxHash/UnsafeBuffer.cs deleted file mode 100644 index f674653..0000000 --- a/src/Standart.Hash.xxHash/UnsafeBuffer.cs +++ /dev/null @@ -1,235 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Diagnostics; - using System.Runtime.CompilerServices; - - internal static class UnsafeBuffer - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void BlockCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - Debug.Assert(src != null); - Debug.Assert(dst != null); - Debug.Assert(srcOffset >= 0 && srcOffset < src.Length); - Debug.Assert(dstOffset >= 0 && dstOffset < dst.Length); - Debug.Assert(count >= 0); - Debug.Assert(count + srcOffset <= src.Length); - Debug.Assert(count + dstOffset <= dst.Length); - - fixed (byte* pSrc = &src[srcOffset]) - fixed (byte* pDst = &dst[dstOffset]) - { - byte* ptrSrc = pSrc; - byte* ptrDst = pDst; - - SMALLTABLE: - switch (count) - { - case 0: - return; - case 1: - *ptrDst = *ptrSrc; - return; - case 2: - *(short*)ptrDst = *(short*)ptrSrc; - return; - case 3: - *(short*)(ptrDst + 0) = *(short*)(ptrSrc + 0); - *(ptrDst + 2) = *(ptrSrc + 2); - return; - case 4: - *(int*)ptrDst = *(int*)ptrSrc; - return; - case 5: - *(int*)(ptrDst + 0) = *(int*)(ptrSrc + 0); - *(ptrDst + 4) = *(ptrSrc + 4); - return; - case 6: - *(int*)(ptrDst + 0) = *(int*)(ptrSrc + 0); - *(short*)(ptrDst + 4) = *(short*)(ptrSrc + 4); - return; - case 7: - *(int*)(ptrDst + 0) = *(int*)(ptrSrc + 0); - *(short*)(ptrDst + 4) = *(short*)(ptrSrc + 4); - *(ptrDst + 6) = *(ptrSrc + 6); - return; - case 8: - *(long*)ptrDst = *(long*)ptrSrc; - return; - case 9: - *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); - *(ptrDst + 8) = *(ptrSrc + 8); - return; - case 10: - *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); - *(short*)(ptrDst + 8) = *(short*)(ptrSrc + 8); - return; - case 11: - *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); - *(short*)(ptrDst + 8) = *(short*)(ptrSrc + 8); - *(ptrDst + 10) = *(ptrSrc + 10); - return; - case 12: - *(long*)ptrDst = *(long*)ptrSrc; - *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); - return; - case 13: - *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); - *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); - *(ptrDst + 12) = *(ptrSrc + 12); - return; - case 14: - *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); - *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); - *(short*)(ptrDst + 12) = *(short*)(ptrSrc + 12); - return; - case 15: - *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); - *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); - *(short*)(ptrDst + 12) = *(short*)(ptrSrc + 12); - *(ptrDst + 14) = *(ptrSrc + 14); - return; - case 16: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - return; - case 17: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(ptrDst + 16) = *(ptrSrc + 16); - return; - case 18: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(short*)(ptrDst + 16) = *(short*)(ptrSrc + 16); - return; - case 19: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(short*)(ptrDst + 16) = *(short*)(ptrSrc + 16); - *(ptrDst + 18) = *(ptrSrc + 18); - return; - case 20: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); - return; - - case 21: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); - *(ptrDst + 20) = *(ptrSrc + 20); - return; - case 22: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); - *(short*)(ptrDst + 20) = *(short*)(ptrSrc + 20); - return; - case 23: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); - *(short*)(ptrDst + 20) = *(short*)(ptrSrc + 20); - *(ptrDst + 22) = *(ptrSrc + 22); - return; - case 24: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - return; - case 25: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(ptrDst + 24) = *(ptrSrc + 24); - return; - case 26: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(short*)(ptrDst + 24) = *(short*)(ptrSrc + 24); - return; - case 27: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(short*)(ptrDst + 24) = *(short*)(ptrSrc + 24); - *(ptrDst + 26) = *(ptrSrc + 26); - return; - case 28: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); - return; - case 29: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); - *(ptrDst + 28) = *(ptrSrc + 28); - return; - case 30: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); - *(short*)(ptrDst + 28) = *(short*)(ptrSrc + 28); - return; - case 31: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); - *(short*)(ptrDst + 28) = *(short*)(ptrSrc + 28); - *(ptrDst + 30) = *(ptrSrc + 30); - return; - case 32: - *(long*)ptrDst = *(long*)ptrSrc; - *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); - *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); - *(long*)(ptrDst + 24) = *(long*)(ptrSrc + 24); - return; - default: - break; - } - - long* lpSrc = (long*)ptrSrc; - long* ldSrc = (long*)ptrDst; - while (count >= 64) - { - *(ldSrc + 0) = *(lpSrc + 0); - *(ldSrc + 1) = *(lpSrc + 1); - *(ldSrc + 2) = *(lpSrc + 2); - *(ldSrc + 3) = *(lpSrc + 3); - *(ldSrc + 4) = *(lpSrc + 4); - *(ldSrc + 5) = *(lpSrc + 5); - *(ldSrc + 6) = *(lpSrc + 6); - *(ldSrc + 7) = *(lpSrc + 7); - if (count == 64) - return; - count -= 64; - lpSrc += 8; - ldSrc += 8; - } - if (count > 32) - { - *(ldSrc + 0) = *(lpSrc + 0); - *(ldSrc + 1) = *(lpSrc + 1); - *(ldSrc + 2) = *(lpSrc + 2); - *(ldSrc + 3) = *(lpSrc + 3); - count -= 32; - lpSrc += 4; - ldSrc += 4; - } - - ptrSrc = (byte*)lpSrc; - ptrDst = (byte*)ldSrc; - goto SMALLTABLE; - } - } - - } -} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/Utils.cs b/src/Standart.Hash.xxHash/Utils.cs index af7141b..ab3b49b 100644 --- a/src/Standart.Hash.xxHash/Utils.cs +++ b/src/Standart.Hash.xxHash/Utils.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace Standart.Hash.xxHash @@ -19,5 +20,231 @@ namespace Standart.Hash.xxHash Unsafe.As(ref bytes[8]) = value.low64; return bytes; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void BlockCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + Debug.Assert(src != null); + Debug.Assert(dst != null); + Debug.Assert(srcOffset >= 0 && srcOffset < src.Length); + Debug.Assert(dstOffset >= 0 && dstOffset < dst.Length); + Debug.Assert(count >= 0); + Debug.Assert(count + srcOffset <= src.Length); + Debug.Assert(count + dstOffset <= dst.Length); + + fixed (byte* pSrc = &src[srcOffset]) + fixed (byte* pDst = &dst[dstOffset]) + { + byte* ptrSrc = pSrc; + byte* ptrDst = pDst; + + SMALLTABLE: + switch (count) + { + case 0: + return; + case 1: + *ptrDst = *ptrSrc; + return; + case 2: + *(short*)ptrDst = *(short*)ptrSrc; + return; + case 3: + *(short*)(ptrDst + 0) = *(short*)(ptrSrc + 0); + *(ptrDst + 2) = *(ptrSrc + 2); + return; + case 4: + *(int*)ptrDst = *(int*)ptrSrc; + return; + case 5: + *(int*)(ptrDst + 0) = *(int*)(ptrSrc + 0); + *(ptrDst + 4) = *(ptrSrc + 4); + return; + case 6: + *(int*)(ptrDst + 0) = *(int*)(ptrSrc + 0); + *(short*)(ptrDst + 4) = *(short*)(ptrSrc + 4); + return; + case 7: + *(int*)(ptrDst + 0) = *(int*)(ptrSrc + 0); + *(short*)(ptrDst + 4) = *(short*)(ptrSrc + 4); + *(ptrDst + 6) = *(ptrSrc + 6); + return; + case 8: + *(long*)ptrDst = *(long*)ptrSrc; + return; + case 9: + *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); + *(ptrDst + 8) = *(ptrSrc + 8); + return; + case 10: + *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); + *(short*)(ptrDst + 8) = *(short*)(ptrSrc + 8); + return; + case 11: + *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); + *(short*)(ptrDst + 8) = *(short*)(ptrSrc + 8); + *(ptrDst + 10) = *(ptrSrc + 10); + return; + case 12: + *(long*)ptrDst = *(long*)ptrSrc; + *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); + return; + case 13: + *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); + *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); + *(ptrDst + 12) = *(ptrSrc + 12); + return; + case 14: + *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); + *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); + *(short*)(ptrDst + 12) = *(short*)(ptrSrc + 12); + return; + case 15: + *(long*)(ptrDst + 0) = *(long*)(ptrSrc + 0); + *(int*)(ptrDst + 8) = *(int*)(ptrSrc + 8); + *(short*)(ptrDst + 12) = *(short*)(ptrSrc + 12); + *(ptrDst + 14) = *(ptrSrc + 14); + return; + case 16: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + return; + case 17: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(ptrDst + 16) = *(ptrSrc + 16); + return; + case 18: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(short*)(ptrDst + 16) = *(short*)(ptrSrc + 16); + return; + case 19: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(short*)(ptrDst + 16) = *(short*)(ptrSrc + 16); + *(ptrDst + 18) = *(ptrSrc + 18); + return; + case 20: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); + return; + + case 21: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); + *(ptrDst + 20) = *(ptrSrc + 20); + return; + case 22: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); + *(short*)(ptrDst + 20) = *(short*)(ptrSrc + 20); + return; + case 23: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(int*)(ptrDst + 16) = *(int*)(ptrSrc + 16); + *(short*)(ptrDst + 20) = *(short*)(ptrSrc + 20); + *(ptrDst + 22) = *(ptrSrc + 22); + return; + case 24: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + return; + case 25: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(ptrDst + 24) = *(ptrSrc + 24); + return; + case 26: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(short*)(ptrDst + 24) = *(short*)(ptrSrc + 24); + return; + case 27: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(short*)(ptrDst + 24) = *(short*)(ptrSrc + 24); + *(ptrDst + 26) = *(ptrSrc + 26); + return; + case 28: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); + return; + case 29: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); + *(ptrDst + 28) = *(ptrSrc + 28); + return; + case 30: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); + *(short*)(ptrDst + 28) = *(short*)(ptrSrc + 28); + return; + case 31: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(int*)(ptrDst + 24) = *(int*)(ptrSrc + 24); + *(short*)(ptrDst + 28) = *(short*)(ptrSrc + 28); + *(ptrDst + 30) = *(ptrSrc + 30); + return; + case 32: + *(long*)ptrDst = *(long*)ptrSrc; + *(long*)(ptrDst + 8) = *(long*)(ptrSrc + 8); + *(long*)(ptrDst + 16) = *(long*)(ptrSrc + 16); + *(long*)(ptrDst + 24) = *(long*)(ptrSrc + 24); + return; + default: + break; + } + + long* lpSrc = (long*)ptrSrc; + long* ldSrc = (long*)ptrDst; + while (count >= 64) + { + *(ldSrc + 0) = *(lpSrc + 0); + *(ldSrc + 1) = *(lpSrc + 1); + *(ldSrc + 2) = *(lpSrc + 2); + *(ldSrc + 3) = *(lpSrc + 3); + *(ldSrc + 4) = *(lpSrc + 4); + *(ldSrc + 5) = *(lpSrc + 5); + *(ldSrc + 6) = *(lpSrc + 6); + *(ldSrc + 7) = *(lpSrc + 7); + if (count == 64) + return; + count -= 64; + lpSrc += 8; + ldSrc += 8; + } + if (count > 32) + { + *(ldSrc + 0) = *(lpSrc + 0); + *(ldSrc + 1) = *(lpSrc + 1); + *(ldSrc + 2) = *(lpSrc + 2); + *(ldSrc + 3) = *(lpSrc + 3); + count -= 32; + lpSrc += 4; + ldSrc += 4; + } + + ptrSrc = (byte*)lpSrc; + ptrDst = (byte*)ldSrc; + goto SMALLTABLE; + } + } } } \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash32.Array.cs b/src/Standart.Hash.xxHash/xxHash32.Array.cs deleted file mode 100644 index c0c8b5f..0000000 --- a/src/Standart.Hash.xxHash/xxHash32.Array.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Diagnostics; - - public static partial class xxHash32 - { - /// - /// Compute xxHash for the data byte array - /// - /// The source of data - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe uint ComputeHash(byte[] data, int length, uint seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(length <= data.Length); - - fixed (byte* pData = &data[0]) - { - return UnsafeComputeHash(pData, length, seed); - } - } - - /// - /// Compute xxHash for the data byte array - /// - /// The source of data - /// The offset of the data for hashing - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe uint ComputeHash(byte[] data, int offset, int length, uint seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(offset < data.Length); - Debug.Assert(length <= data.Length - offset); - - fixed (byte* pData = &data[0 + offset]) - { - return UnsafeComputeHash(pData, length, seed); - } - } - /// - /// Compute xxHash for the data byte array - /// - /// The source of data - /// The seed number - /// hash - public static unsafe ulong ComputeHash(System.ArraySegment data, uint seed = 0) - { - Debug.Assert(data != null); - - return ComputeHash(data.Array, data.Offset, data.Count, seed); - } - - } -} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash32.Async.cs b/src/Standart.Hash.xxHash/xxHash32.Async.cs deleted file mode 100644 index 9f51024..0000000 --- a/src/Standart.Hash.xxHash/xxHash32.Async.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Buffers; - using System.Diagnostics; - using System.IO; - using System.Threading; - using System.Threading.Tasks; - - public static partial class xxHash32 - { - /// - /// Compute xxHash for the async stream - /// - /// The stream of data - /// The buffer size - /// The seed number - /// The hash - public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0) - { - return await ComputeHashAsync(stream, bufferSize, seed, CancellationToken.None); - } - - - /// - /// Compute xxHash for the async stream - /// - /// The stream of data - /// The buffer size - /// The seed number - /// The cancellation token - /// The hash - public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize, uint seed, CancellationToken cancellationToken) - { - Debug.Assert(stream != null); - Debug.Assert(bufferSize > 16); - - // Optimizing memory allocation - byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16); - - int readBytes; - int offset = 0; - long length = 0; - - // Prepare the seed vector - uint v1 = seed + p1 + p2; - uint v2 = seed + p2; - uint v3 = seed + 0; - uint v4 = seed - p1; - - try - { - // Read flow of bytes - while ((readBytes = await stream.ReadAsync(buffer, offset, bufferSize, cancellationToken).ConfigureAwait(false)) > 0) - { - // Exit if the operation is canceled - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - length = length + readBytes; - offset = offset + readBytes; - - if (offset < 16) continue; - - int r = offset % 16; // remain - int l = offset - r; // length - - // Process the next chunk - UnsafeAlign(buffer, l, ref v1, ref v2, ref v3, ref v4); - - // Put remaining bytes to buffer - UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); - offset = r; - } - - // Process the final chunk - uint h32 = UnsafeFinal(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); - - return h32; - } - finally - { - // Free memory - ArrayPool.Shared.Return(buffer); - } - } - } -} diff --git a/src/Standart.Hash.xxHash/xxHash32.Span.cs b/src/Standart.Hash.xxHash/xxHash32.Span.cs deleted file mode 100644 index 1e7114d..0000000 --- a/src/Standart.Hash.xxHash/xxHash32.Span.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Diagnostics; - -namespace Standart.Hash.xxHash -{ - using System.Runtime.InteropServices; - - public static partial class xxHash32 - { - /// - /// Compute xxHash for the data byte span - /// - /// The source of data - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe uint ComputeHash(Span data, int length, uint seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(length <= data.Length); - - fixed (byte* pData = &MemoryMarshal.GetReference(data)) - { - return UnsafeComputeHash(pData, length, seed); - } - } - - /// - /// Compute xxHash for the data byte span - /// - /// The source of data - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe uint ComputeHash(ReadOnlySpan data, int length, uint seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(length <= data.Length); - - fixed (byte* pData = &MemoryMarshal.GetReference(data)) - { - return UnsafeComputeHash(pData, length, seed); - } - } - } -} diff --git a/src/Standart.Hash.xxHash/xxHash32.Stream.cs b/src/Standart.Hash.xxHash/xxHash32.Stream.cs deleted file mode 100644 index 467b67a..0000000 --- a/src/Standart.Hash.xxHash/xxHash32.Stream.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Buffers; - using System.Diagnostics; - using System.IO; - - public static partial class xxHash32 - { - /// - /// Compute xxHash for the stream - /// - /// The stream of data - /// The buffer size - /// The seed number - /// The hash - public static uint ComputeHash(Stream stream, int bufferSize = 4096, uint seed = 0) - { - Debug.Assert(stream != null); - Debug.Assert(bufferSize > 16); - - // Optimizing memory allocation - byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16); - - int readBytes; - int offset = 0; - long length = 0; - - // Prepare the seed vector - uint v1 = seed + p1 + p2; - uint v2 = seed + p2; - uint v3 = seed + 0; - uint v4 = seed - p1; - - try - { - // Read flow of bytes - while ((readBytes = stream.Read(buffer, offset, bufferSize)) > 0) - { - length = length + readBytes; - offset = offset + readBytes; - - if (offset < 16) continue; - - int r = offset % 16; // remain - int l = offset - r; // length - - // Process the next chunk - UnsafeAlign(buffer, l, ref v1, ref v2, ref v3, ref v4); - - // Put remaining bytes to buffer - UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); - offset = r; - } - - // Process the last chunk - uint h32 = UnsafeFinal(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); - - return h32; - } - finally - { - // Free memory - ArrayPool.Shared.Return(buffer); - } - } - } -} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash32.Unsafe.cs b/src/Standart.Hash.xxHash/xxHash32.Unsafe.cs deleted file mode 100644 index b5e13a1..0000000 --- a/src/Standart.Hash.xxHash/xxHash32.Unsafe.cs +++ /dev/null @@ -1,200 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Runtime.CompilerServices; - - public static partial class xxHash32 - { - private const uint p1 = 2654435761U; - private const uint p2 = 2246822519U; - private const uint p3 = 3266489917U; - private const uint p4 = 668265263U; - private const uint p5 = 374761393U; - - /// - /// Compute xxhash32 for the unsafe array of memory - /// - /// - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe uint UnsafeComputeHash(byte* ptr, int length, uint seed) - { - byte* end = ptr + length; - uint h32; - - if (length >= 16) - { - byte* limit = end - 16; - - uint v1 = seed + p1 + p2; - uint v2 = seed + p2; - uint v3 = seed + 0; - uint v4 = seed - p1; - - do - { - v1 += *((uint*)ptr) * p2; - v1 = BitUtils.RotateLeft(v1, 13); // rotl 13 - v1 *= p1; - ptr += 4; - - v2 += *((uint*)ptr) * p2; - v2 = BitUtils.RotateLeft(v2, 13); // rotl 13 - v2 *= p1; - ptr += 4; - - v3 += *((uint*)ptr) * p2; - v3 = BitUtils.RotateLeft(v3, 13); // rotl 13 - v3 *= p1; - ptr += 4; - - v4 += *((uint*)ptr) * p2; - v4 = BitUtils.RotateLeft(v4, 13); // rotl 13 - v4 *= p1; - ptr += 4; - - } while (ptr <= limit); - - h32 = BitUtils.RotateLeft(v1, 1) + // rotl 1 - BitUtils.RotateLeft(v2, 7) + // rotl 7 - BitUtils.RotateLeft(v3, 12) + // rotl 12 - BitUtils.RotateLeft(v4, 18); // rotl 18 - } - else - { - h32 = seed + p5; - } - - h32 += (uint)length; - - // finalize - while (ptr <= end - 4) - { - h32 += *((uint*)ptr) * p3; - h32 = BitUtils.RotateLeft(h32, 17) * p4; // (rotl 17) * p4 - ptr += 4; - } - - while (ptr < end) - { - h32 += *((byte*)ptr) * p5; - h32 = BitUtils.RotateLeft(h32, 11) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h32 ^= h32 >> 15; - h32 *= p2; - h32 ^= h32 >> 13; - h32 *= p3; - h32 ^= h32 >> 16; - - return h32; - } - - /// - /// Compute the first part of xxhash32 (need for the streaming api) - /// - /// - /// - /// - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void UnsafeAlign(byte[] data, int l, ref uint v1, ref uint v2, ref uint v3, ref uint v4) - { - fixed (byte* pData = &data[0]) - { - byte* ptr = pData; - byte* limit = ptr + l; - - do - { - v1 += *((uint*)ptr) * p2; - v1 = BitUtils.RotateLeft(v1, 13); // rotl 13 - v1 *= p1; - ptr += 4; - - v2 += *((uint*)ptr) * p2; - v2 = BitUtils.RotateLeft(v2, 13); // rotl 13 - v2 *= p1; - ptr += 4; - - v3 += *((uint*)ptr) * p2; - v3 = BitUtils.RotateLeft(v3, 13); // rotl 13 - v3 *= p1; - ptr += 4; - - v4 += *((uint*)ptr) * p2; - v4 = BitUtils.RotateLeft(v4, 13); // rotl 13 - v4 *= p1; - ptr += 4; - - } while (ptr < limit); - } - } - - /// - /// Compute the second part of xxhash32 (need for the streaming api) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe uint UnsafeFinal(byte[] data, int l, ref uint v1, ref uint v2, ref uint v3, ref uint v4, long length, uint seed) - { - fixed (byte* pData = &data[0]) - { - byte* ptr = pData; - byte* end = pData + l; - uint h32; - - if (length >= 16) - { - h32 = BitUtils.RotateLeft(v1, 1) + // rotl 1 - BitUtils.RotateLeft(v2, 7) + // rotl 7 - BitUtils.RotateLeft(v3, 12) + // rotl 12 - BitUtils.RotateLeft(v4, 18); // rotl 18 - } - else - { - h32 = seed + p5; - } - - h32 += (uint)length; - - // finalize - while (ptr <= end - 4) - { - h32 += *((uint*)ptr) * p3; - h32 = BitUtils.RotateLeft(h32, 17) * p4; // (rotl 17) * p4 - ptr += 4; - } - - while (ptr < end) - { - h32 += *((byte*)ptr) * p5; - h32 = BitUtils.RotateLeft(h32, 11) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h32 ^= h32 >> 15; - h32 *= p2; - h32 ^= h32 >> 13; - h32 *= p3; - h32 ^= h32 >> 16; - - return h32; - } - } - } -} diff --git a/src/Standart.Hash.xxHash/xxHash32.XXH.cs b/src/Standart.Hash.xxHash/xxHash32.XXH.cs new file mode 100644 index 0000000..712d32c --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash32.XXH.cs @@ -0,0 +1,20 @@ +// ReSharper disable InconsistentNaming + +using System.Runtime.CompilerServices; + +namespace Standart.Hash.xxHash; + +public static partial class xxHash32 +{ + private const uint XXH_PRIME32_1 = 2654435761U; + private const uint XXH_PRIME32_2 = 2246822519U; + private const uint XXH_PRIME32_3 = 3266489917U; + private const uint XXH_PRIME32_4 = 668265263U; + private const uint XXH_PRIME32_5 = 374761393U; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint XXH_rotl32(uint x, int r) + { + return (x << r) | (x >> (32 - r)); + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash32.XXH32.cs b/src/Standart.Hash.xxHash/xxHash32.XXH32.cs new file mode 100644 index 0000000..f9b950f --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash32.XXH32.cs @@ -0,0 +1,176 @@ +// ReSharper disable InconsistentNaming + +namespace Standart.Hash.xxHash +{ + using System.Runtime.CompilerServices; + + public static partial class xxHash32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint XXH32_internal(byte* input, int len, uint seed) + { + byte* end = input + len; + uint h32; + + if (len >= 16) + { + byte* limit = end - 16; + + uint v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + uint v2 = seed + XXH_PRIME32_2; + uint v3 = seed + 0; + uint v4 = seed - XXH_PRIME32_1; + + do + { + // XXH32_round + v1 += *((uint*)input) * XXH_PRIME32_2; + v1 = XXH_rotl32(v1, 13); + v1 *= XXH_PRIME32_1; + input += 4; + + // XXH32_round + v2 += *((uint*)input) * XXH_PRIME32_2; + v2 = XXH_rotl32(v2, 13); + v2 *= XXH_PRIME32_1; + input += 4; + + // XXH32_round + v3 += *((uint*)input) * XXH_PRIME32_2; + v3 = XXH_rotl32(v3, 13); + v3 *= XXH_PRIME32_1; + input += 4; + + // XXH32_round + v4 += *((uint*)input) * XXH_PRIME32_2; + v4 = XXH_rotl32(v4, 13); + v4 *= XXH_PRIME32_1; + input += 4; + + } while (input <= limit); + + h32 = XXH_rotl32(v1, 1) + + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + + XXH_rotl32(v4, 18); + } + else + { + h32 = seed + XXH_PRIME32_5; + } + + h32 += (uint)len; + + // XXH32_finalize + while (input <= end - 4) + { + h32 += *((uint*)input) * XXH_PRIME32_3; + h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; + input += 4; + } + + while (input < end) + { + h32 += *((byte*)input) * XXH_PRIME32_5; + h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; + input += 1; + } + + // XXH32_avalanche + h32 ^= h32 >> 15; + h32 *= XXH_PRIME32_2; + h32 ^= h32 >> 13; + h32 *= XXH_PRIME32_3; + h32 ^= h32 >> 16; + + return h32; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void __XXH32_stream_align(byte[] input, int len, ref uint v1, ref uint v2, ref uint v3, ref uint v4) + { + fixed (byte* pData = &input[0]) + { + byte* ptr = pData; + byte* limit = ptr + len; + + do + { + // XXH32_round + v1 += *((uint*)ptr) * XXH_PRIME32_2; + v1 = XXH_rotl32(v1, 13); + v1 *= XXH_PRIME32_1; + ptr += 4; + + // XXH32_round + v2 += *((uint*)ptr) * XXH_PRIME32_2; + v2 = XXH_rotl32(v2, 13); + v2 *= XXH_PRIME32_1; + ptr += 4; + + // XXH32_round + v3 += *((uint*)ptr) * XXH_PRIME32_2; + v3 = XXH_rotl32(v3, 13); + v3 *= XXH_PRIME32_1; + ptr += 4; + + // XXH32_round + v4 += *((uint*)ptr) * XXH_PRIME32_2; + v4 = XXH_rotl32(v4, 13); + v4 *= XXH_PRIME32_1; + ptr += 4; + + } while (ptr < limit); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint __XXH32_stream_finalize(byte[] input, int len, ref uint v1, ref uint v2, ref uint v3, ref uint v4, long length, uint seed) + { + fixed (byte* pData = &input[0]) + { + byte* ptr = pData; + byte* end = pData + len; + uint h32; + + if (length >= 16) + { + h32 = XXH_rotl32(v1, 1) + + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + + XXH_rotl32(v4, 18); + } + else + { + h32 = seed + XXH_PRIME32_5; + } + + h32 += (uint)length; + + // XXH32_finalize + while (ptr <= end - 4) + { + h32 += *((uint*)ptr) * XXH_PRIME32_3; + h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; + ptr += 4; + } + + while (ptr < end) + { + h32 += *((byte*)ptr) * XXH_PRIME32_5; + h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; + ptr += 1; + } + + // XXH32_avalanche + h32 ^= h32 >> 15; + h32 *= XXH_PRIME32_2; + h32 ^= h32 >> 13; + h32 *= XXH_PRIME32_3; + h32 ^= h32 >> 16; + + return h32; + } + } + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash32.cs b/src/Standart.Hash.xxHash/xxHash32.cs new file mode 100644 index 0000000..87e6ace --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash32.cs @@ -0,0 +1,262 @@ +// ReSharper disable InconsistentNaming + +using System; +using System.Buffers; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Standart.Hash.xxHash; + +public static partial class xxHash32 +{ + /// + /// Compute xxHash for the data byte array + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe uint ComputeHash(byte[] data, int length, uint seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* pData = &data[0]) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the data byte array + /// + /// The source of data + /// The offset of the data for hashing + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe uint ComputeHash(byte[] data, int offset, int length, uint seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(offset < data.Length); + Debug.Assert(length <= data.Length - offset); + + fixed (byte* pData = &data[0 + offset]) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the data byte array + /// + /// The source of data + /// The seed number + /// hash + public static ulong ComputeHash(ArraySegment data, uint seed = 0) + { + Debug.Assert(data != null); + + return ComputeHash(data.Array, data.Offset, data.Count, seed); + } + + /// + /// Compute xxHash for the async stream + /// + /// The stream of data + /// The buffer size + /// The seed number + /// The hash + public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0) + { + return await ComputeHashAsync(stream, bufferSize, seed, CancellationToken.None); + } + + /// + /// Compute xxHash for the async stream + /// + /// The stream of data + /// The buffer size + /// The seed number + /// The cancellation token + /// The hash + public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize, uint seed, + CancellationToken cancellationToken) + { + Debug.Assert(stream != null); + Debug.Assert(bufferSize > 16); + + // Optimizing memory allocation + byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16); + + int readBytes; + int offset = 0; + long length = 0; + + // Prepare the seed vector + uint v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + uint v2 = seed + XXH_PRIME32_2; + uint v3 = seed + 0; + uint v4 = seed - XXH_PRIME32_1; + + try + { + // Read flow of bytes + while ((readBytes = + await stream.ReadAsync(buffer, offset, bufferSize, cancellationToken).ConfigureAwait(false)) > 0) + { + length = length + readBytes; + offset = offset + readBytes; + + if (offset < 16) continue; + + int r = offset % 16; // remain + int l = offset - r; // length + + // Process the next chunk + __XXH32_stream_align(buffer, l, ref v1, ref v2, ref v3, ref v4); + + // Put remaining bytes to buffer + Utils.BlockCopy(buffer, l, buffer, 0, r); + offset = r; + } + + // Process the final chunk + uint h32 = __XXH32_stream_finalize(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); + + return h32; + } + finally + { + // Free memory + ArrayPool.Shared.Return(buffer); + } + } + + /// + /// Compute xxHash for the data byte span + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe uint ComputeHash(Span data, int length, uint seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* pData = &MemoryMarshal.GetReference(data)) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the data byte span + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe uint ComputeHash(ReadOnlySpan data, int length, uint seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* pData = &MemoryMarshal.GetReference(data)) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the stream + /// + /// The stream of data + /// The buffer size + /// The seed number + /// The hash + public static uint ComputeHash(Stream stream, int bufferSize = 4096, uint seed = 0) + { + Debug.Assert(stream != null); + Debug.Assert(bufferSize > 16); + + // Optimizing memory allocation + byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16); + + int readBytes; + int offset = 0; + long length = 0; + + // Prepare the seed vector + uint v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + uint v2 = seed + XXH_PRIME32_2; + uint v3 = seed + 0; + uint v4 = seed - XXH_PRIME32_1; + + try + { + // Read flow of bytes + while ((readBytes = stream.Read(buffer, offset, bufferSize)) > 0) + { + length = length + readBytes; + offset = offset + readBytes; + + if (offset < 16) continue; + + int r = offset % 16; // remain + int l = offset - r; // length + + // Process the next chunk + __XXH32_stream_align(buffer, l, ref v1, ref v2, ref v3, ref v4); + + // Put remaining bytes to buffer + Utils.BlockCopy(buffer, l, buffer, 0, r); + offset = r; + } + + // Process the last chunk + uint h32 = __XXH32_stream_finalize(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); + + return h32; + } + finally + { + // Free memory + ArrayPool.Shared.Return(buffer); + } + } + + /// + /// Compute xxHash for the string + /// + /// The source of data + /// The seed number + /// hash + public static unsafe uint ComputeHash(string str, uint seed = 0) + { + Debug.Assert(str != null); + + fixed (char* c = str) + { + byte* ptr = (byte*) c; + int length = str.Length; + + return UnsafeComputeHash(ptr, length, seed); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint UnsafeComputeHash(byte* ptr, int length, uint seed) + { + return XXH32_internal(ptr, length, seed); + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash64.Array.cs b/src/Standart.Hash.xxHash/xxHash64.Array.cs deleted file mode 100644 index f7f3a11..0000000 --- a/src/Standart.Hash.xxHash/xxHash64.Array.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Diagnostics; - - public static partial class xxHash64 - { - /// - /// Compute xxHash for the data byte array - /// - /// The source of data - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe ulong ComputeHash(byte[] data, int length, ulong seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(length <= data.Length); - - fixed (byte* pData = &data[0]) - { - return UnsafeComputeHash(pData, length, seed); - } - } - /// - /// Compute xxHash for the data byte array - /// - /// The source of data - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe ulong ComputeHash(byte[] data, int offset, int length, ulong seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(offset < data.Length); - Debug.Assert(length <= data.Length - offset); - - fixed (byte* pData = &data[0 + offset]) - { - return UnsafeComputeHash(pData, length, seed); - } - } - /// - /// Compute xxHash for the data byte array - /// - /// The source of data - /// The seed number - /// hash - public static unsafe ulong ComputeHash(System.ArraySegment data, ulong seed = 0) - { - Debug.Assert(data != null); - - return ComputeHash(data.Array, data.Offset, data.Count, seed); - } - - } -} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash64.Async.cs b/src/Standart.Hash.xxHash/xxHash64.Async.cs deleted file mode 100644 index 896611c..0000000 --- a/src/Standart.Hash.xxHash/xxHash64.Async.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Buffers; - using System.Diagnostics; - using System.IO; - using System.Threading; - using System.Threading.Tasks; - - public static partial class xxHash64 - { - /// - /// Compute xxHash for the async stream - /// - /// The stream of data - /// The buffer size - /// The seed number - /// The hash - public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize = 8192, ulong seed = 0) - { - return await ComputeHashAsync(stream, bufferSize, seed, CancellationToken.None); - } - - /// - /// Compute xxHash for the async stream - /// - /// The stream of data - /// The buffer size - /// The seed number - /// The cancelation token - /// The hash - public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize, ulong seed, CancellationToken cancellationToken) - { - Debug.Assert(stream != null); - Debug.Assert(bufferSize > 32); - - // Optimizing memory allocation - byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 32); - - int readBytes; - int offset = 0; - long length = 0; - - // Prepare the seed vector - ulong v1 = seed + p1 + p2; - ulong v2 = seed + p2; - ulong v3 = seed + 0; - ulong v4 = seed - p1; - - try - { - // Read flow of bytes - while ((readBytes = await stream.ReadAsync(buffer, offset, bufferSize, cancellationToken).ConfigureAwait(false)) > 0) - { - // Exit if the operation is canceled - if (cancellationToken.IsCancellationRequested) - { - return await Task.FromCanceled(cancellationToken); - } - - length = length + readBytes; - offset = offset + readBytes; - - if (offset < 32) continue; - - int r = offset % 32; // remain - int l = offset - r; // length - - // Process the next chunk - UnsafeAlign(buffer, l, ref v1, ref v2, ref v3, ref v4); - - // Put remaining bytes to buffer - UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); - offset = r; - } - - // Process the final chunk - ulong h64 = UnsafeFinal(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); - - return h64; - } - finally - { - // Free memory - ArrayPool.Shared.Return(buffer); - } - } - } -} diff --git a/src/Standart.Hash.xxHash/xxHash64.Span.cs b/src/Standart.Hash.xxHash/xxHash64.Span.cs deleted file mode 100644 index d8dbf0e..0000000 --- a/src/Standart.Hash.xxHash/xxHash64.Span.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System; - using System.Diagnostics; - using System.Runtime.InteropServices; - - public static partial class xxHash64 - { - /// - /// Compute xxHash for the data byte span - /// - /// The source of data - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe ulong ComputeHash(Span data, int length, ulong seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(length <= data.Length); - - fixed (byte* pData = &MemoryMarshal.GetReference(data)) - { - return UnsafeComputeHash(pData, length, seed); - } - } - - /// - /// Compute xxHash for the data byte span - /// - /// The source of data - /// The length of the data for hashing - /// The seed number - /// hash - public static unsafe ulong ComputeHash(ReadOnlySpan data, int length, ulong seed = 0) - { - Debug.Assert(data != null); - Debug.Assert(length >= 0); - Debug.Assert(length <= data.Length); - - fixed (byte* pData = &MemoryMarshal.GetReference(data)) - { - return UnsafeComputeHash(pData, length, seed); - } - } - } -} diff --git a/src/Standart.Hash.xxHash/xxHash64.Stream.cs b/src/Standart.Hash.xxHash/xxHash64.Stream.cs deleted file mode 100644 index 66d92c8..0000000 --- a/src/Standart.Hash.xxHash/xxHash64.Stream.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Buffers; - using System.Diagnostics; - using System.IO; - - public static partial class xxHash64 - { - /// - /// Compute xxHash for the stream - /// - /// The stream of data - /// The buffer size - /// The seed number - /// The hash - public static ulong ComputeHash(Stream stream, int bufferSize = 8192, ulong seed = 0) - { - Debug.Assert(stream != null); - Debug.Assert(bufferSize > 32); - - // Optimizing memory allocation - byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 32); - - int readBytes; - int offset = 0; - long length = 0; - - // Prepare the seed vector - ulong v1 = seed + p1 + p2; - ulong v2 = seed + p2; - ulong v3 = seed + 0; - ulong v4 = seed - p1; - - try - { - // Read flow of bytes - while ((readBytes = stream.Read(buffer, offset, bufferSize)) > 0) - { - length = length + readBytes; - offset = offset + readBytes; - - if (offset < 32) continue; - - int r = offset % 32; // remain - int l = offset - r; // length - - // Process the next chunk - UnsafeAlign(buffer, l, ref v1, ref v2, ref v3, ref v4); - - // Put remaining bytes to buffer - UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); - offset = r; - } - - // Process the final chunk - ulong h64 = UnsafeFinal(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); - - return h64; - } - finally - { - // Free memory - ArrayPool.Shared.Return(buffer); - } - } - } -} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash64.Unsafe.cs b/src/Standart.Hash.xxHash/xxHash64.Unsafe.cs deleted file mode 100644 index ae09a7b..0000000 --- a/src/Standart.Hash.xxHash/xxHash64.Unsafe.cs +++ /dev/null @@ -1,278 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Runtime.CompilerServices; - - public static partial class xxHash64 - { - private const ulong p1 = 11400714785074694791UL; - private const ulong p2 = 14029467366897019727UL; - private const ulong p3 = 1609587929392839161UL; - private const ulong p4 = 9650029242287828579UL; - private const ulong p5 = 2870177450012600261UL; - - /// - /// Compute xxhash64 for the unsafe array of memory - /// - /// - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe ulong UnsafeComputeHash(byte* ptr, int length, ulong seed) - { - byte* end = ptr + length; - ulong h64; - - if (length >= 32) - - { - byte* limit = end - 32; - - ulong v1 = seed + p1 + p2; - ulong v2 = seed + p2; - ulong v3 = seed + 0; - ulong v4 = seed - p1; - - do - { - v1 += *((ulong*)ptr) * p2; - v1 = BitUtils.RotateLeft(v1, 31); // rotl 31 - v1 *= p1; - ptr += 8; - - v2 += *((ulong*)ptr) * p2; - v2 = BitUtils.RotateLeft(v2, 31); // rotl 31 - v2 *= p1; - ptr += 8; - - v3 += *((ulong*)ptr) * p2; - v3 = BitUtils.RotateLeft(v3, 31); // rotl 31 - v3 *= p1; - ptr += 8; - - v4 += *((ulong*)ptr) * p2; - v4 = BitUtils.RotateLeft(v4, 31); // rotl 31 - v4 *= p1; - ptr += 8; - - } while (ptr <= limit); - - h64 = BitUtils.RotateLeft(v1, 1) + // rotl 1 - BitUtils.RotateLeft(v2, 7) + // rotl 7 - BitUtils.RotateLeft(v3, 12) + // rotl 12 - BitUtils.RotateLeft(v4, 18); // rotl 18 - - // merge round - v1 *= p2; - v1 = BitUtils.RotateLeft(v1, 31); // rotl 31 - v1 *= p1; - h64 ^= v1; - h64 = h64 * p1 + p4; - - // merge round - v2 *= p2; - v2 = BitUtils.RotateLeft(v2, 31); // rotl 31 - v2 *= p1; - h64 ^= v2; - h64 = h64 * p1 + p4; - - // merge round - v3 *= p2; - v3 = BitUtils.RotateLeft(v3, 31); // rotl 31 - v3 *= p1; - h64 ^= v3; - h64 = h64 * p1 + p4; - - // merge round - v4 *= p2; - v4 = BitUtils.RotateLeft(v4, 31); // rotl 31 - v4 *= p1; - h64 ^= v4; - h64 = h64 * p1 + p4; - } - else - { - h64 = seed + p5; - } - - h64 += (ulong)length; - - // finalize - while (ptr <= end - 8) - { - ulong t1 = *((ulong*)ptr) * p2; - t1 = BitUtils.RotateLeft(t1, 31); // rotl 31 - t1 *= p1; - h64 ^= t1; - h64 = BitUtils.RotateLeft(h64, 27) * p1 + p4; // (rotl 27) * p1 + p4 - ptr += 8; - } - - if (ptr <= end - 4) - { - h64 ^= *((uint*)ptr) * p1; - h64 = BitUtils.RotateLeft(h64, 23) * p2 + p3; // (rotl 23) * p2 + p3 - ptr += 4; - } - - while (ptr < end) - { - h64 ^= *((byte*)ptr) * p5; - h64 = BitUtils.RotateLeft(h64, 11) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h64 ^= h64 >> 33; - h64 *= p2; - h64 ^= h64 >> 29; - h64 *= p3; - h64 ^= h64 >> 32; - - return h64; - } - - /// - /// Compute the first part of xxhash64 (need for the streaming api) - /// - /// - /// - /// - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void UnsafeAlign(byte[] data, int l, ref ulong v1, ref ulong v2, ref ulong v3, ref ulong v4) - { - fixed (byte* pData = &data[0]) - { - byte* ptr = pData; - byte* limit = ptr + l; - - do - { - v1 += *((ulong*)ptr) * p2; - v1 = BitUtils.RotateLeft(v1, 31); // rotl 31 - v1 *= p1; - ptr += 8; - - v2 += *((ulong*)ptr) * p2; - v2 = BitUtils.RotateLeft(v2, 31); // rotl 31 - v2 *= p1; - ptr += 8; - - v3 += *((ulong*)ptr) * p2; - v3 = BitUtils.RotateLeft(v3, 31); // rotl 31 - v3 *= p1; - ptr += 8; - - v4 += *((ulong*)ptr) * p2; - v4 = BitUtils.RotateLeft(v4, 31); // rotl 31 - v4 *= p1; - ptr += 8; - - } while (ptr < limit); - } - } - - /// - /// Compute the second part of xxhash64 (need for the streaming api) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe ulong UnsafeFinal(byte[] data, int l, ref ulong v1, ref ulong v2, ref ulong v3, ref ulong v4, long length, ulong seed) - { - fixed (byte* pData = &data[0]) - { - byte* ptr = pData; - byte* end = pData + l; - ulong h64; - - if (length >= 32) - { - h64 = BitUtils.RotateLeft(v1, 1) + // rotl 1 - BitUtils.RotateLeft(v2, 7) + // rotl 7 - BitUtils.RotateLeft(v3, 12) + // rotl 12 - BitUtils.RotateLeft(v4, 18); // rotl 18 - - // merge round - v1 *= p2; - v1 = BitUtils.RotateLeft(v1, 31); // rotl 31 - v1 *= p1; - h64 ^= v1; - h64 = h64 * p1 + p4; - - // merge round - v2 *= p2; - v2 = BitUtils.RotateLeft(v2, 31); // rotl 31 - v2 *= p1; - h64 ^= v2; - h64 = h64 * p1 + p4; - - // merge round - v3 *= p2; - v3 = BitUtils.RotateLeft(v3, 31); // rotl 31 - v3 *= p1; - h64 ^= v3; - h64 = h64 * p1 + p4; - - // merge round - v4 *= p2; - v4 = BitUtils.RotateLeft(v4, 31); // rotl 31 - v4 *= p1; - h64 ^= v4; - h64 = h64 * p1 + p4; - - } - else - { - h64 = seed + p5; - } - - h64 += (ulong)length; - - // finalize - while (ptr <= end - 8) - { - ulong t1 = *((ulong*)ptr) * p2; - t1 = BitUtils.RotateLeft(t1, 31); // rotl 31 - t1 *= p1; - h64 ^= t1; - h64 = BitUtils.RotateLeft(h64, 27) * p1 + p4; // (rotl 27) * p1 + p4 - ptr += 8; - } - - if (ptr <= end - 4) - { - h64 ^= *((uint*)ptr) * p1; - h64 = BitUtils.RotateLeft(h64, 23) * p2 + p3; // (rotl 27) * p2 + p3 - ptr += 4; - } - - while (ptr < end) - { - h64 ^= *((byte*)ptr) * p5; - h64 = BitUtils.RotateLeft(h64, 11) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h64 ^= h64 >> 33; - h64 *= p2; - h64 ^= h64 >> 29; - h64 *= p3; - h64 ^= h64 >> 32; - - return h64; - } - } - } -} diff --git a/src/Standart.Hash.xxHash/xxHash64.XXH.cs b/src/Standart.Hash.xxHash/xxHash64.XXH.cs new file mode 100644 index 0000000..edfd7ed --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash64.XXH.cs @@ -0,0 +1,20 @@ +// ReSharper disable InconsistentNaming + +using System.Runtime.CompilerServices; + +namespace Standart.Hash.xxHash; + +public static partial class xxHash64 +{ + private const ulong XXH_PRIME64_1 = 11400714785074694791UL; + private const ulong XXH_PRIME64_2 = 14029467366897019727UL; + private const ulong XXH_PRIME64_3 = 1609587929392839161UL; + private const ulong XXH_PRIME64_4 = 9650029242287828579UL; + private const ulong XXH_PRIME64_5 = 2870177450012600261UL; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH_rotl64(ulong x, int r) + { + return (x << r) | (x >> (64 - r)); + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash64.XXH64.cs b/src/Standart.Hash.xxHash/xxHash64.XXH64.cs new file mode 100644 index 0000000..7bc1c8e --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash64.XXH64.cs @@ -0,0 +1,253 @@ +// ReSharper disable InconsistentNaming + +namespace Standart.Hash.xxHash +{ + using System.Runtime.CompilerServices; + + public static partial class xxHash64 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong XXH64_internal(byte* input, int len, ulong seed) + { + byte* end = input + len; + ulong h64; + + if (len >= 32) + + { + byte* limit = end - 32; + + ulong v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + ulong v2 = seed + XXH_PRIME64_2; + ulong v3 = seed + 0; + ulong v4 = seed - XXH_PRIME64_1; + + do + { + // XXH64_round + v1 += *((ulong*)input) * XXH_PRIME64_2; + v1 = XXH_rotl64(v1, 31); + v1 *= XXH_PRIME64_1; + input += 8; + + // XXH64_round + v2 += *((ulong*)input) * XXH_PRIME64_2; + v2 = XXH_rotl64(v2, 31); + v2 *= XXH_PRIME64_1; + input += 8; + + // XXH64_round + v3 += *((ulong*)input) * XXH_PRIME64_2; + v3 = XXH_rotl64(v3, 31); + v3 *= XXH_PRIME64_1; + input += 8; + + // XXH64_round + v4 += *((ulong*)input) * XXH_PRIME64_2; + v4 = XXH_rotl64(v4, 31); + v4 *= XXH_PRIME64_1; + input += 8; + + } while (input <= limit); + + h64 = XXH_rotl64(v1, 1) + + XXH_rotl64(v2, 7) + + XXH_rotl64(v3, 12) + + XXH_rotl64(v4, 18); + + // XXH64_mergeRound + v1 *= XXH_PRIME64_2; + v1 = XXH_rotl64(v1, 31); + v1 *= XXH_PRIME64_1; + h64 ^= v1; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + + // XXH64_mergeRound + v2 *= XXH_PRIME64_2; + v2 = XXH_rotl64(v2, 31); + v2 *= XXH_PRIME64_1; + h64 ^= v2; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + + // XXH64_mergeRound + v3 *= XXH_PRIME64_2; + v3 = XXH_rotl64(v3, 31); + v3 *= XXH_PRIME64_1; + h64 ^= v3; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + + // XXH64_mergeRound + v4 *= XXH_PRIME64_2; + v4 = XXH_rotl64(v4, 31); + v4 *= XXH_PRIME64_1; + h64 ^= v4; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + } + else + { + h64 = seed + XXH_PRIME64_5; + } + + h64 += (ulong)len; + + // XXH64_finalize + while (input <= end - 8) + { + ulong t1 = *((ulong*)input) * XXH_PRIME64_2; + t1 = XXH_rotl64(t1, 31); + t1 *= XXH_PRIME64_1; + h64 ^= t1; + h64 = XXH_rotl64(h64, 27) * XXH_PRIME64_1 + XXH_PRIME64_4; + input += 8; + } + + if (input <= end - 4) + { + h64 ^= *((uint*)input) * XXH_PRIME64_1; + h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; + input += 4; + } + + while (input < end) + { + h64 ^= *((byte*)input) * XXH_PRIME64_5; + h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; + input += 1; + } + + // XXH64_avalanche + h64 ^= h64 >> 33; + h64 *= XXH_PRIME64_2; + h64 ^= h64 >> 29; + h64 *= XXH_PRIME64_3; + h64 ^= h64 >> 32; + + return h64; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void __XXH64_stream_align(byte[] input, int len, ref ulong v1, ref ulong v2, ref ulong v3, ref ulong v4) + { + fixed (byte* pData = &input[0]) + { + byte* ptr = pData; + byte* limit = ptr + len; + + do + { + // XXH64_round + v1 += *((ulong*)ptr) * XXH_PRIME64_2; + v1 = XXH_rotl64(v1, 31); + v1 *= XXH_PRIME64_1; + ptr += 8; + + // XXH64_round + v2 += *((ulong*)ptr) * XXH_PRIME64_2; + v2 = XXH_rotl64(v2, 31); + v2 *= XXH_PRIME64_1; + ptr += 8; + + // XXH64_round + v3 += *((ulong*)ptr) * XXH_PRIME64_2; + v3 = XXH_rotl64(v3, 31); + v3 *= XXH_PRIME64_1; + ptr += 8; + + // XXH64_round + v4 += *((ulong*)ptr) * XXH_PRIME64_2; + v4 = XXH_rotl64(v4, 31); + v4 *= XXH_PRIME64_1; + ptr += 8; + + } while (ptr < limit); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong __XXH64_stream_finalize(byte[] input, int len, ref ulong v1, ref ulong v2, ref ulong v3, ref ulong v4, long length, ulong seed) + { + fixed (byte* pData = &input[0]) + { + byte* ptr = pData; + byte* end = pData + len; + ulong h64; + + if (length >= 32) + { + h64 = XXH_rotl64(v1, 1) + + XXH_rotl64(v2, 7) + + XXH_rotl64(v3, 12) + + XXH_rotl64(v4, 18); + + // XXH64_mergeRound + v1 *= XXH_PRIME64_2; + v1 = XXH_rotl64(v1, 31); + v1 *= XXH_PRIME64_1; + h64 ^= v1; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + + // XXH64_mergeRound + v2 *= XXH_PRIME64_2; + v2 = XXH_rotl64(v2, 31); + v2 *= XXH_PRIME64_1; + h64 ^= v2; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + + // XXH64_mergeRound + v3 *= XXH_PRIME64_2; + v3 = XXH_rotl64(v3, 31); + v3 *= XXH_PRIME64_1; + h64 ^= v3; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + + // XXH64_mergeRound + v4 *= XXH_PRIME64_2; + v4 = XXH_rotl64(v4, 31); + v4 *= XXH_PRIME64_1; + h64 ^= v4; + h64 = h64 * XXH_PRIME64_1 + XXH_PRIME64_4; + } + else + { + h64 = seed + XXH_PRIME64_5; + } + + h64 += (ulong)length; + + // XXH64_finalize + while (ptr <= end - 8) + { + ulong t1 = *((ulong*)ptr) * XXH_PRIME64_2; + t1 = XXH_rotl64(t1, 31); + t1 *= XXH_PRIME64_1; + h64 ^= t1; + h64 = XXH_rotl64(h64, 27) * XXH_PRIME64_1 + XXH_PRIME64_4; + ptr += 8; + } + + if (ptr <= end - 4) + { + h64 ^= *((uint*)ptr) * XXH_PRIME64_1; + h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; + ptr += 4; + } + + while (ptr < end) + { + h64 ^= *((byte*)ptr) * XXH_PRIME64_5; + h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; + ptr += 1; + } + + // XXH64_avalanche + h64 ^= h64 >> 33; + h64 *= XXH_PRIME64_2; + h64 ^= h64 >> 29; + h64 *= XXH_PRIME64_3; + h64 ^= h64 >> 32; + + return h64; + } + } + } +} diff --git a/src/Standart.Hash.xxHash/xxHash64.cs b/src/Standart.Hash.xxHash/xxHash64.cs new file mode 100644 index 0000000..074d0cc --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash64.cs @@ -0,0 +1,261 @@ +// ReSharper disable InconsistentNaming + +using System; +using System.Buffers; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Standart.Hash.xxHash; + +public static partial class xxHash64 +{ + /// + /// Compute xxHash for the data byte array + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe ulong ComputeHash(byte[] data, int length, ulong seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* pData = &data[0]) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the data byte array + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe ulong ComputeHash(byte[] data, int offset, int length, ulong seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(offset < data.Length); + Debug.Assert(length <= data.Length - offset); + + fixed (byte* pData = &data[0 + offset]) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the data byte array + /// + /// The source of data + /// The seed number + /// hash + public static unsafe ulong ComputeHash(System.ArraySegment data, ulong seed = 0) + { + Debug.Assert(data != null); + + return ComputeHash(data.Array, data.Offset, data.Count, seed); + } + + /// + /// Compute xxHash for the async stream + /// + /// The stream of data + /// The buffer size + /// The seed number + /// The hash + public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize = 8192, ulong seed = 0) + { + return await ComputeHashAsync(stream, bufferSize, seed, CancellationToken.None); + } + + /// + /// Compute xxHash for the async stream + /// + /// The stream of data + /// The buffer size + /// The seed number + /// The cancelation token + /// The hash + public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize, ulong seed, + CancellationToken cancellationToken) + { + Debug.Assert(stream != null); + Debug.Assert(bufferSize > 32); + + // Optimizing memory allocation + byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 32); + + int readBytes; + int offset = 0; + long length = 0; + + // Prepare the seed vector + ulong v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + ulong v2 = seed + XXH_PRIME64_2; + ulong v3 = seed + 0; + ulong v4 = seed - XXH_PRIME64_1; + + try + { + // Read flow of bytes + while ((readBytes = + await stream.ReadAsync(buffer, offset, bufferSize, cancellationToken).ConfigureAwait(false)) > 0) + { + length = length + readBytes; + offset = offset + readBytes; + + if (offset < 32) continue; + + int r = offset % 32; // remain + int l = offset - r; // length + + // Process the next chunk + __XXH64_stream_align(buffer, l, ref v1, ref v2, ref v3, ref v4); + + // Put remaining bytes to buffer + Utils.BlockCopy(buffer, l, buffer, 0, r); + offset = r; + } + + // Process the final chunk + ulong h64 = __XXH64_stream_finalize(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); + + return h64; + } + finally + { + // Free memory + ArrayPool.Shared.Return(buffer); + } + } + + /// + /// Compute xxHash for the data byte span + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe ulong ComputeHash(Span data, int length, ulong seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* pData = &MemoryMarshal.GetReference(data)) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the data byte span + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe ulong ComputeHash(ReadOnlySpan data, int length, ulong seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* pData = &MemoryMarshal.GetReference(data)) + { + return UnsafeComputeHash(pData, length, seed); + } + } + + /// + /// Compute xxHash for the stream + /// + /// The stream of data + /// The buffer size + /// The seed number + /// The hash + public static ulong ComputeHash(Stream stream, int bufferSize = 8192, ulong seed = 0) + { + Debug.Assert(stream != null); + Debug.Assert(bufferSize > 32); + + // Optimizing memory allocation + byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 32); + + int readBytes; + int offset = 0; + long length = 0; + + // Prepare the seed vector + ulong v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + ulong v2 = seed + XXH_PRIME64_2; + ulong v3 = seed + 0; + ulong v4 = seed - XXH_PRIME64_1; + + try + { + // Read flow of bytes + while ((readBytes = stream.Read(buffer, offset, bufferSize)) > 0) + { + length = length + readBytes; + offset = offset + readBytes; + + if (offset < 32) continue; + + int r = offset % 32; // remain + int l = offset - r; // length + + // Process the next chunk + __XXH64_stream_align(buffer, l, ref v1, ref v2, ref v3, ref v4); + + // Put remaining bytes to buffer + Utils.BlockCopy(buffer, l, buffer, 0, r); + offset = r; + } + + // Process the final chunk + ulong h64 = __XXH64_stream_finalize(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); + + return h64; + } + finally + { + // Free memory + ArrayPool.Shared.Return(buffer); + } + } + + /// + /// Compute xxHash for the string + /// + /// The source of data + /// The seed number + /// hash + public static unsafe ulong ComputeHash(string str, uint seed = 0) + { + Debug.Assert(str != null); + + fixed (char* c = str) + { + byte* ptr = (byte*) c; + int length = str.Length; + + return UnsafeComputeHash(ptr, length, seed); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong UnsafeComputeHash(byte* ptr, int length, ulong seed) + { + return XXH64_internal(ptr, length, seed); + } +} \ No newline at end of file