From 4b3505719152365b85752caefc8b37f20b6c07a8 Mon Sep 17 00:00:00 2001 From: Melnik Alexander Date: Sat, 29 Sep 2018 01:02:35 +0300 Subject: [PATCH] Added Span api --- README.md | 6 +- deps.props | 12 +- nuget.props | 2 +- ...rBenchmark.cs => UnsafeBufferBenchmark.cs} | 8 +- .../xxHashBenchmark.cs | 20 +- .../{xxBufferTest.cs => UnsafeBufferTest.cs} | 4 +- src/Standart.Hash.xxHash.Test/xxHash32Test.cs | 50 ++-- src/Standart.Hash.xxHash.Test/xxHash64Test.cs | 35 ++- src/Standart.Hash.xxHash/AssemblyInfo.cs | 4 + .../Standart.Hash.xxHash.csproj | 4 +- src/Standart.Hash.xxHash/UnsafeBuffer.cs | 235 ++++++++++++++++++ src/Standart.Hash.xxHash/xxBuffer.cs | 228 ----------------- src/Standart.Hash.xxHash/xxHash32.Stream.cs | 14 +- src/Standart.Hash.xxHash/xxHash32.cs | 40 ++- src/Standart.Hash.xxHash/xxHash64.Stream.cs | 18 +- src/Standart.Hash.xxHash/xxHash64.cs | 38 ++- 16 files changed, 428 insertions(+), 290 deletions(-) rename src/Standart.Hash.xxHash.Perf/{xxBufferBenchmark.cs => UnsafeBufferBenchmark.cs} (73%) rename src/Standart.Hash.xxHash.Test/{xxBufferTest.cs => UnsafeBufferTest.cs} (86%) create mode 100644 src/Standart.Hash.xxHash/AssemblyInfo.cs create mode 100644 src/Standart.Hash.xxHash/UnsafeBuffer.cs delete mode 100644 src/Standart.Hash.xxHash/xxBuffer.cs diff --git a/README.md b/README.md index 9e7ac50..865b64a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ coverage - nuget + nuget platform @@ -39,16 +39,20 @@ This benchmark was launched on a **Windows 10 (10.0.16299.309)**. The reference |---------------|----------:| | Hash32 Array | 5.05 GB/s | | Hash64 Array | 8.92 GB/s | +| Hash32 Span | 5.05 GB/s | +| Hash64 Span | 8.92 GB/s | | Hash32 Stream | 3.22 GB/s | | Hash64 Stream | 4.81 GB/s | ## Api ```cs public static uint ComputeHash(byte[] data, int length, uint seed = 0) { throw null; } +public static uint ComputeHash(Span data, int length, uint seed = 0) { throw null; } public static uint ComputeHash(Stream stream, int bufferSize = 4096, uint seed = 0) { throw null; } public static async Task ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0) { throw null; } public static ulong ComputeHash(byte[] data, int length, ulong seed = 0) { throw null; } +public static ulong ComputeHash(Span data, int length, ulong seed = 0) { throw null; } public static ulong ComputeHash(Stream stream, int bufferSize = 8192, ulong seed = 0) { throw null; } public static async Task ComputeHashAsync(Stream stream, int bufferSize = 8192, ulong seed = 0) { throw null; } ``` diff --git a/deps.props b/deps.props index f74040c..3413d54 100644 --- a/deps.props +++ b/deps.props @@ -1,11 +1,13 @@ - 4.4.0 + 4.5.0 4.3.0 - 0.10.14 - 15.6.0 - 2.3.1 - 2.3.1 + 4.5.1 + 4.5.1 + 0.11.1 + 15.8.0 + 2.4.0 + 2.4.0 2.3.1 \ No newline at end of file diff --git a/nuget.props b/nuget.props index cc75e4d..1a6385d 100644 --- a/nuget.props +++ b/nuget.props @@ -3,7 +3,7 @@ netstandard2.0 Standart.Hash.xxHash - 1.0.4 + 1.0.5 Standart.Hash.xxHash Standart.Hash.xxHash Alexander Melnik diff --git a/src/Standart.Hash.xxHash.Perf/xxBufferBenchmark.cs b/src/Standart.Hash.xxHash.Perf/UnsafeBufferBenchmark.cs similarity index 73% rename from src/Standart.Hash.xxHash.Perf/xxBufferBenchmark.cs rename to src/Standart.Hash.xxHash.Perf/UnsafeBufferBenchmark.cs index a25018c..e145ec3 100644 --- a/src/Standart.Hash.xxHash.Perf/xxBufferBenchmark.cs +++ b/src/Standart.Hash.xxHash.Perf/UnsafeBufferBenchmark.cs @@ -2,13 +2,11 @@ { using System; using BenchmarkDotNet.Attributes; - using BenchmarkDotNet.Attributes.Columns; - using BenchmarkDotNet.Attributes.Exporters; [RPlotExporter, RankColumn] [MinColumn, MaxColumn] [MemoryDiagnoser] - public class xxBufferBenchmark + public class UnsafeBufferBenchmark { private byte[] src; private byte[] des; @@ -33,9 +31,9 @@ } [Benchmark] - public void xxBufferCopy() + public void UnsafeBufferCopy() { - xxBuffer.BlockCopy(src, 0, des, 0, 32); + UnsafeBuffer.BlockCopy(src, 0, des, 0, 32); } } } \ No newline at end of file diff --git a/src/Standart.Hash.xxHash.Perf/xxHashBenchmark.cs b/src/Standart.Hash.xxHash.Perf/xxHashBenchmark.cs index 5450743..4e2249e 100644 --- a/src/Standart.Hash.xxHash.Perf/xxHashBenchmark.cs +++ b/src/Standart.Hash.xxHash.Perf/xxHashBenchmark.cs @@ -4,8 +4,6 @@ using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; - using BenchmarkDotNet.Attributes.Columns; - using BenchmarkDotNet.Attributes.Exporters; [RPlotExporter, RankColumn] [MinColumn, MaxColumn] @@ -32,11 +30,18 @@ } [Benchmark] - public uint Hash32() + public uint Hash32_Array() { return xxHash32.ComputeHash(data, data.Length); } + [Benchmark] + public uint Hash32_Span() + { + Span span = new Span(data); + return xxHash32.ComputeHash(span, span.Length); + } + [Benchmark] public uint Hash32_Stream() { @@ -52,11 +57,18 @@ } [Benchmark] - public ulong Hash64() + public ulong Hash64_Array() { return xxHash64.ComputeHash(data, data.Length); } + [Benchmark] + public ulong Hash64_Span() + { + Span span = new Span(data); + return xxHash64.ComputeHash(span, span.Length); + } + [Benchmark] public ulong Hash64_Stream() { diff --git a/src/Standart.Hash.xxHash.Test/xxBufferTest.cs b/src/Standart.Hash.xxHash.Test/UnsafeBufferTest.cs similarity index 86% rename from src/Standart.Hash.xxHash.Test/xxBufferTest.cs rename to src/Standart.Hash.xxHash.Test/UnsafeBufferTest.cs index 6ca6a42..0000b57 100644 --- a/src/Standart.Hash.xxHash.Test/xxBufferTest.cs +++ b/src/Standart.Hash.xxHash.Test/UnsafeBufferTest.cs @@ -3,7 +3,7 @@ using System; using Xunit; - public class xxBufferTest + public class UnsafeBufferTest { [Fact] public void Copy_different_blocks() @@ -27,7 +27,7 @@ // Act, Assert foreach (int count in counts) { - xxBuffer.BlockCopy(src, 0, dst, 0, count); + UnsafeBuffer.BlockCopy(src, 0, dst, 0, count); for (int i = 0; i < count; i++) { diff --git a/src/Standart.Hash.xxHash.Test/xxHash32Test.cs b/src/Standart.Hash.xxHash.Test/xxHash32Test.cs index 4a0bb9e..61214a8 100644 --- a/src/Standart.Hash.xxHash.Test/xxHash32Test.cs +++ b/src/Standart.Hash.xxHash.Test/xxHash32Test.cs @@ -12,12 +12,15 @@ { // Arrange byte[] data = {0xde}; + Span span = new Span(data); // Act - uint hash = xxHash32.ComputeHash(data, data.Length); + uint hash1 = xxHash32.ComputeHash(data, data.Length); + uint hash2 = xxHash32.ComputeHash(span, span.Length); // Assert - Assert.Equal(hash, (uint) 0x2330eac0); + Assert.Equal(hash1, (uint) 0x2330eac0); + Assert.Equal(hash2, (uint) 0x2330eac0); } [Fact] @@ -25,12 +28,15 @@ { // Arrange byte[] data = {0xde, 0x55, 0x47, 0x7f, 0x14}; + Span span = new Span(data); // Act - uint hash = xxHash32.ComputeHash(data, data.Length); + uint hash1 = xxHash32.ComputeHash(data, data.Length); + uint hash2 = xxHash32.ComputeHash(span, span.Length); // Assert - Assert.Equal(hash, (uint) 0x112348ba); + Assert.Equal(hash1, (uint) 0x112348ba); + Assert.Equal(hash2, (uint) 0x112348ba); } [Fact] @@ -42,12 +48,15 @@ 0xde, 0x55, 0x47, 0x7f, 0x14, 0x8f, 0xf1, 0x48, 0x22, 0x3a, 0x40, 0x96, 0x56, 0xc5, 0xdc, 0xbb }; - + Span span = new Span(data); + // Act - uint hash = xxHash32.ComputeHash(data, data.Length); + uint hash1 = xxHash32.ComputeHash(data, data.Length); + uint hash2 = xxHash32.ComputeHash(span, span.Length); // Assert - Assert.Equal(hash, (uint) 0xcdf89609); + Assert.Equal(hash1, (uint) 0xcdf89609); + Assert.Equal(hash2, (uint) 0xcdf89609); } [Fact] @@ -60,12 +69,15 @@ 0x22, 0x3a, 0x40, 0x96, 0x56, 0xc5, 0xdc, 0xbb, 0x0e }; - + Span span = new Span(data); + // Act - uint hash = xxHash32.ComputeHash(data, data.Length); + uint hash1 = xxHash32.ComputeHash(data, data.Length); + uint hash2 = xxHash32.ComputeHash(span, span.Length); // Assert - Assert.Equal(hash, (uint) 0xbca8f924); + Assert.Equal(hash1, (uint) 0xbca8f924); + Assert.Equal(hash2, (uint) 0xbca8f924); } [Fact] @@ -78,12 +90,15 @@ 0x22, 0x3a, 0x40, 0x96, 0x56, 0xc5, 0xdc, 0xbb, 0x0e, 0x59, 0x4d, 0x42, 0xc5 }; - + Span span = new Span(data); + // Act - uint hash = xxHash32.ComputeHash(data, data.Length); + uint hash1 = xxHash32.ComputeHash(data, data.Length); + uint hash2 = xxHash32.ComputeHash(span, span.Length); // Assert - Assert.Equal(hash, (uint) 0xf4518e14); + Assert.Equal(hash1, (uint) 0xf4518e14); + Assert.Equal(hash2, (uint) 0xf4518e14); } [Fact] @@ -97,12 +112,15 @@ 0x0e, 0x59, 0x4d, 0x42, 0xc5, 0x07, 0x21, 0x08, 0x1c, 0x2c, 0xc9, 0x38, 0x7d, 0x43, 0x83, 0x11, }; - + Span span = new Span(data); + // Act - uint hash = xxHash32.ComputeHash(data, data.Length); + uint hash1 = xxHash32.ComputeHash(data, data.Length); + uint hash2 = xxHash32.ComputeHash(span, span.Length); // Assert - Assert.Equal(hash, (uint) 0xf8497daa); + Assert.Equal(hash1, (uint) 0xf8497daa); + Assert.Equal(hash2, (uint) 0xf8497daa); } [Fact] diff --git a/src/Standart.Hash.xxHash.Test/xxHash64Test.cs b/src/Standart.Hash.xxHash.Test/xxHash64Test.cs index be65204..013159f 100644 --- a/src/Standart.Hash.xxHash.Test/xxHash64Test.cs +++ b/src/Standart.Hash.xxHash.Test/xxHash64Test.cs @@ -12,12 +12,15 @@ { // Arrange byte[] data = {0x60}; + Span span = new Span(data); // Act - ulong hash = xxHash64.ComputeHash(data, data.Length); + ulong hash1 = xxHash64.ComputeHash(data, data.Length); + ulong hash2 = xxHash64.ComputeHash(span, data.Length); // Assert - Assert.Equal(hash, (ulong) 0xb3e7ca6ca5ba3445); + Assert.Equal(hash1, (ulong) 0xb3e7ca6ca5ba3445); + Assert.Equal(hash2, (ulong) 0xb3e7ca6ca5ba3445); } [Fact] @@ -25,12 +28,15 @@ { // Arrange byte[] data = {0x60, 0x82, 0x40, 0x77, 0x8a}; + Span span = new Span(data); // Act - ulong hash = xxHash64.ComputeHash(data, data.Length); + ulong hash1 = xxHash64.ComputeHash(data, data.Length); + ulong hash2 = xxHash64.ComputeHash(span, data.Length); // Assert - Assert.Equal(hash, (ulong) 0x917b11ed024938fc); + Assert.Equal(hash1, (ulong) 0x917b11ed024938fc); + Assert.Equal(hash2, (ulong) 0x917b11ed024938fc); } [Fact] @@ -42,12 +48,15 @@ 0x60, 0x82, 0x40, 0x77, 0x8a, 0x0e, 0xe4, 0xd5, 0x85, 0x1f, 0xa6, 0x86, 0x34, }; + Span span = new Span(data); // Act - ulong hash = xxHash64.ComputeHash(data, data.Length); + ulong hash1 = xxHash64.ComputeHash(data, data.Length); + ulong hash2 = xxHash64.ComputeHash(span, data.Length); // Assert - Assert.Equal(hash, (ulong) 0x9d1cb0d181d58bee); + Assert.Equal(hash1, (ulong) 0x9d1cb0d181d58bee); + Assert.Equal(hash2, (ulong) 0x9d1cb0d181d58bee); } [Fact] @@ -61,12 +70,15 @@ 0x30, 0x5d, 0x84, 0x54, 0x15, 0xf9, 0xbd, 0x03, 0x4b, 0x0f, 0x90, 0x4e, 0xf5, 0x57, 0x21, 0x21, }; + Span span = new Span(data); // Act - ulong hash = xxHash64.ComputeHash(data, data.Length); + ulong hash1 = xxHash64.ComputeHash(data, data.Length); + ulong hash2 = xxHash64.ComputeHash(span, data.Length); // Assert - Assert.Equal(hash, (ulong) 0x9233096b7804e12c); + Assert.Equal(hash1, (ulong) 0x9233096b7804e12c); + Assert.Equal(hash2, (ulong) 0x9233096b7804e12c); } [Fact] @@ -84,12 +96,15 @@ 0x27, 0x6d, 0xb3, 0x5c, 0xc7, 0xc0, 0xd0, 0xa0, 0x7e, 0x28, 0xce, 0x46, 0x85, 0xb7, 0x2b, 0x16, }; + Span span = new Span(data); // Act - ulong hash = xxHash64.ComputeHash(data, data.Length); + ulong hash1 = xxHash64.ComputeHash(data, data.Length); + ulong hash2 = xxHash64.ComputeHash(span, data.Length); // Assert - Assert.Equal(hash, (ulong) 0x4c0a65b1ef9ea060); + Assert.Equal(hash1, (ulong) 0x4c0a65b1ef9ea060); + Assert.Equal(hash2, (ulong) 0x4c0a65b1ef9ea060); } [Fact] diff --git a/src/Standart.Hash.xxHash/AssemblyInfo.cs b/src/Standart.Hash.xxHash/AssemblyInfo.cs new file mode 100644 index 0000000..62476bf --- /dev/null +++ b/src/Standart.Hash.xxHash/AssemblyInfo.cs @@ -0,0 +1,4 @@ +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/Standart.Hash.xxHash.csproj b/src/Standart.Hash.xxHash/Standart.Hash.xxHash.csproj index dc2d24c..5e797e4 100644 --- a/src/Standart.Hash.xxHash/Standart.Hash.xxHash.csproj +++ b/src/Standart.Hash.xxHash/Standart.Hash.xxHash.csproj @@ -13,6 +13,8 @@ - + + + \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/UnsafeBuffer.cs b/src/Standart.Hash.xxHash/UnsafeBuffer.cs new file mode 100644 index 0000000..f674653 --- /dev/null +++ b/src/Standart.Hash.xxHash/UnsafeBuffer.cs @@ -0,0 +1,235 @@ +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/xxBuffer.cs b/src/Standart.Hash.xxHash/xxBuffer.cs deleted file mode 100644 index fb65929..0000000 --- a/src/Standart.Hash.xxHash/xxBuffer.cs +++ /dev/null @@ -1,228 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System.Runtime.CompilerServices; - - public static class xxBuffer - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void BlockCopy(this byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - fixed (byte* ptrSrc = &src[srcOffset]) - fixed (byte* ptrDst = &dst[dstOffset]) - { - UnsafeBlockCopy(ptrSrc, ptrDst, count); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void UnsafeBlockCopy(byte* src, byte* dst, int count) - { - SMALLTABLE: - switch (count) - { - case 0: - return; - case 1: - *dst = *src; - return; - case 2: - *(short*)dst = *(short*)src; - return; - case 3: - *(short*)(dst + 0) = *(short*)(src + 0); - *(dst + 2) = *(src + 2); - return; - case 4: - *(int*)dst = *(int*)src; - return; - case 5: - *(int*)(dst + 0) = *(int*)(src + 0); - *(dst + 4) = *(src + 4); - return; - case 6: - *(int*)(dst + 0) = *(int*)(src + 0); - *(short*)(dst + 4) = *(short*)(src + 4); - return; - case 7: - *(int*)(dst + 0) = *(int*)(src + 0); - *(short*)(dst + 4) = *(short*)(src + 4); - *(dst + 6) = *(src + 6); - return; - case 8: - *(long*)dst = *(long*)src; - return; - case 9: - *(long*)(dst + 0) = *(long*)(src + 0); - *(dst + 8) = *(src + 8); - return; - case 10: - *(long*)(dst + 0) = *(long*)(src + 0); - *(short*)(dst + 8) = *(short*)(src + 8); - return; - case 11: - *(long*)(dst + 0) = *(long*)(src + 0); - *(short*)(dst + 8) = *(short*)(src + 8); - *(dst + 10) = *(src + 10); - return; - case 12: - *(long*)dst = *(long*)src; - *(int*)(dst + 8) = *(int*)(src + 8); - return; - case 13: - *(long*)(dst + 0) = *(long*)(src + 0); - *(int*)(dst + 8) = *(int*)(src + 8); - *(dst + 12) = *(src + 12); - return; - case 14: - *(long*)(dst + 0) = *(long*)(src + 0); - *(int*)(dst + 8) = *(int*)(src + 8); - *(short*)(dst + 12) = *(short*)(src + 12); - return; - case 15: - *(long*)(dst + 0) = *(long*)(src + 0); - *(int*)(dst + 8) = *(int*)(src + 8); - *(short*)(dst + 12) = *(short*)(src + 12); - *(dst + 14) = *(src + 14); - return; - case 16: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - return; - case 17: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(dst + 16) = *(src + 16); - return; - case 18: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(short*)(dst + 16) = *(short*)(src + 16); - return; - case 19: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(short*)(dst + 16) = *(short*)(src + 16); - *(dst + 18) = *(src + 18); - return; - case 20: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(int*)(dst + 16) = *(int*)(src + 16); - return; - - case 21: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(int*)(dst + 16) = *(int*)(src + 16); - *(dst + 20) = *(src + 20); - return; - case 22: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(int*)(dst + 16) = *(int*)(src + 16); - *(short*)(dst + 20) = *(short*)(src + 20); - return; - case 23: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(int*)(dst + 16) = *(int*)(src + 16); - *(short*)(dst + 20) = *(short*)(src + 20); - *(dst + 22) = *(src + 22); - return; - case 24: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - return; - case 25: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(dst + 24) = *(src + 24); - return; - case 26: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(short*)(dst + 24) = *(short*)(src + 24); - return; - case 27: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(short*)(dst + 24) = *(short*)(src + 24); - *(dst + 26) = *(src + 26); - return; - case 28: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(int*)(dst + 24) = *(int*)(src + 24); - return; - case 29: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(int*)(dst + 24) = *(int*)(src + 24); - *(dst + 28) = *(src + 28); - return; - case 30: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(int*)(dst + 24) = *(int*)(src + 24); - *(short*)(dst + 28) = *(short*)(src + 28); - return; - case 31: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(int*)(dst + 24) = *(int*)(src + 24); - *(short*)(dst + 28) = *(short*)(src + 28); - *(dst + 30) = *(src + 30); - return; - case 32: - *(long*)dst = *(long*)src; - *(long*)(dst + 8) = *(long*)(src + 8); - *(long*)(dst + 16) = *(long*)(src + 16); - *(long*)(dst + 24) = *(long*)(src + 24); - return; - default: - break; - } - - long* lpSrc = (long*)src; - long* ldSrc = (long*)dst; - 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; - } - - src = (byte*)lpSrc; - dst = (byte*)ldSrc; - goto SMALLTABLE; - } - } -} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash32.Stream.cs b/src/Standart.Hash.xxHash/xxHash32.Stream.cs index 77e1ef3..15d1dda 100644 --- a/src/Standart.Hash.xxHash/xxHash32.Stream.cs +++ b/src/Standart.Hash.xxHash/xxHash32.Stream.cs @@ -1,7 +1,7 @@ namespace Standart.Hash.xxHash { - using System; using System.Buffers; + using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -101,6 +101,9 @@ /// 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); @@ -131,7 +134,7 @@ Shift(buffer, l, ref v1, ref v2, ref v3, ref v4); // Put remaining bytes to buffer - xxBuffer.BlockCopy(buffer, l, buffer, 0, r); + UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); offset = r; } @@ -154,8 +157,11 @@ /// The buffer size /// The seed number /// The hash - public static async Task ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0) + public static async ValueTask ComputeHashAsync(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); @@ -186,7 +192,7 @@ Shift(buffer, l, ref v1, ref v2, ref v3, ref v4); // Put remaining bytes to buffer - xxBuffer.BlockCopy(buffer, l, buffer, 0, r); + UnsafeBuffer.BlockCopy(buffer,l, buffer, 0, r); offset = r; } diff --git a/src/Standart.Hash.xxHash/xxHash32.cs b/src/Standart.Hash.xxHash/xxHash32.cs index 9eac1b8..47153f0 100644 --- a/src/Standart.Hash.xxHash/xxHash32.cs +++ b/src/Standart.Hash.xxHash/xxHash32.cs @@ -1,5 +1,9 @@ namespace Standart.Hash.xxHash { + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + public static partial class xxHash32 { private const uint p1 = 2654435761U; @@ -8,7 +12,7 @@ private const uint p4 = 668265263U; private const uint p5 = 374761393U; - /// d + /// /// Compute xxHash for the data byte array /// /// The source of data @@ -17,10 +21,39 @@ /// 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]) { - byte* ptr = pData; - byte* end = pData + length; + 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(Span 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); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint UnsafeComputeHash(byte* ptr, int length, uint seed) + { + byte* end = ptr + length; uint h32; if (length >= 16) @@ -91,7 +124,6 @@ h32 ^= h32 >> 16; return h32; - } } } } \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash64.Stream.cs b/src/Standart.Hash.xxHash/xxHash64.Stream.cs index 0cf3b88..f089143 100644 --- a/src/Standart.Hash.xxHash/xxHash64.Stream.cs +++ b/src/Standart.Hash.xxHash/xxHash64.Stream.cs @@ -1,7 +1,7 @@ namespace Standart.Hash.xxHash { - using System; using System.Buffers; + using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -45,14 +45,14 @@ [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe ulong Final(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 >= 16) + if (length >= 32) { h64 = ((v1 << 1) | (v1 >> (64 - 1))) + // rotl 1 ((v2 << 7) | (v2 >> (64 - 7))) + // rotl 7 @@ -140,6 +140,9 @@ /// 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); @@ -170,7 +173,7 @@ Shift(buffer, l, ref v1, ref v2, ref v3, ref v4); // Put remaining bytes to buffer - xxBuffer.BlockCopy(buffer, l, buffer, 0, r); + UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); offset = r; } @@ -193,8 +196,11 @@ /// The buffer size /// The seed number /// The hash - public static async Task ComputeHashAsync(Stream stream, int bufferSize = 8192, ulong seed = 0) + public static async ValueTask ComputeHashAsync(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); @@ -225,7 +231,7 @@ Shift(buffer, l, ref v1, ref v2, ref v3, ref v4); // Put remaining bytes to buffer - xxBuffer.BlockCopy(buffer, l, buffer, 0, r); + UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); offset = r; } diff --git a/src/Standart.Hash.xxHash/xxHash64.cs b/src/Standart.Hash.xxHash/xxHash64.cs index 8e643ba..c2ce918 100644 --- a/src/Standart.Hash.xxHash/xxHash64.cs +++ b/src/Standart.Hash.xxHash/xxHash64.cs @@ -1,5 +1,9 @@ namespace Standart.Hash.xxHash { + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + public static partial class xxHash64 { private const ulong p1 = 11400714785074694791UL; @@ -17,10 +21,39 @@ /// 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]) { - byte* ptr = pData; - byte* end = pData + length; + 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(Span 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); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong UnsafeComputeHash(byte* ptr, int length, ulong seed) + { + byte* end = ptr + length; ulong h64; if (length >= 32) @@ -129,7 +162,6 @@ h64 ^= h64 >> 32; return h64; - } } } } \ No newline at end of file