diff --git a/src/Standart.Hash.xxHash.Perf/Program.cs b/src/Standart.Hash.xxHash.Perf/Program.cs index a6b6b8f..24bdcb0 100644 --- a/src/Standart.Hash.xxHash.Perf/Program.cs +++ b/src/Standart.Hash.xxHash.Perf/Program.cs @@ -6,7 +6,8 @@ { public static void Main(string[] args) { - var summary = BenchmarkRunner.Run(); + BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } } } \ 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 4e2249e..241489d 100644 --- a/src/Standart.Hash.xxHash.Perf/xxHashBenchmark.cs +++ b/src/Standart.Hash.xxHash.Perf/xxHashBenchmark.cs @@ -24,8 +24,10 @@ [GlobalSetup] public void Setup() { + Random rand = new Random(42); + data = new byte[N]; - new Random(42).NextBytes(data); + rand.NextBytes(data); stream = new MemoryStream(data); } diff --git a/src/Standart.Hash.xxHash/xxHash32.Array.cs b/src/Standart.Hash.xxHash/xxHash32.Array.cs new file mode 100644 index 0000000..64edd71 --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash32.Array.cs @@ -0,0 +1,26 @@ +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); + } + } + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash32.Async.cs b/src/Standart.Hash.xxHash/xxHash32.Async.cs new file mode 100644 index 0000000..164832b --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash32.Async.cs @@ -0,0 +1,68 @@ +namespace Standart.Hash.xxHash +{ + using System.Buffers; + using System.Diagnostics; + using System.IO; + 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) + { + 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)) > 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 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 new file mode 100644 index 0000000..a59fc50 --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash32.Span.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics; + +namespace Standart.Hash.xxHash +{ + 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 = &data[0]) + { + return UnsafeComputeHash(pData, length, seed); + } + } + } +} diff --git a/src/Standart.Hash.xxHash/xxHash32.Stream.cs b/src/Standart.Hash.xxHash/xxHash32.Stream.cs index 15d1dda..467b67a 100644 --- a/src/Standart.Hash.xxHash/xxHash32.Stream.cs +++ b/src/Standart.Hash.xxHash/xxHash32.Stream.cs @@ -3,95 +3,9 @@ using System.Buffers; using System.Diagnostics; using System.IO; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; public static partial class xxHash32 { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void Shift(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 = (v1 << 13) | (v1 >> (32 - 13)); // rotl 13 - v1 *= p1; - ptr += 4; - - v2 += *((uint*) ptr) * p2; - v2 = (v2 << 13) | (v2 >> (32 - 13)); // rotl 13 - v2 *= p1; - ptr += 4; - - v3 += *((uint*) ptr) * p2; - v3 = (v3 << 13) | (v3 >> (32 - 13)); // rotl 13 - v3 *= p1; - ptr += 4; - - v4 += *((uint*) ptr) * p2; - v4 = (v4 << 13) | (v4 >> (32 - 13)); // rotl 13 - v4 *= p1; - ptr += 4; - - } while (ptr < limit); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe uint Final(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 = ((v1 << 1) | (v1 >> (32 - 1))) + // rotl 1 - ((v2 << 7) | (v2 >> (32 - 7))) + // rotl 7 - ((v3 << 12) | (v3 >> (32 - 12))) + // rotl 12 - ((v4 << 18) | (v4 >> (32 - 18))); // rotl 18 - } - else - { - h32 = seed + p5; - } - - h32 += (uint) length; - - // finalize - while (ptr <= end - 4) - { - h32 += *((uint*)ptr) * p3; - h32 = ((h32 << 17) | (h32 >> (32 - 17))) * p4; // (rotl 17) * p4 - ptr += 4; - } - - while (ptr < end) - { - h32 += *((byte*)ptr) * p5; - h32 = ((h32 << 11) | (h32 >> (32 - 11))) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h32 ^= h32 >> 15; - h32 *= p2; - h32 ^= h32 >> 13; - h32 *= p3; - h32 ^= h32 >> 16; - - return h32; - } - } - - /// /// Compute xxHash for the stream /// @@ -131,73 +45,15 @@ int l = offset - r; // length // Process the next chunk - Shift(buffer, l, ref v1, ref v2, ref v3, ref v4); + 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 = Final(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 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) - { - 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)) > 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 - Shift(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 = Final(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); + // Process the last chunk + uint h32 = UnsafeFinal(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); return h32; } diff --git a/src/Standart.Hash.xxHash/xxHash32.Unsafe.cs b/src/Standart.Hash.xxHash/xxHash32.Unsafe.cs new file mode 100644 index 0000000..741cf95 --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash32.Unsafe.cs @@ -0,0 +1,200 @@ +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 = (v1 << 13) | (v1 >> (32 - 13)); // rotl 13 + v1 *= p1; + ptr += 4; + + v2 += *((uint*)ptr) * p2; + v2 = (v2 << 13) | (v2 >> (32 - 13)); // rotl 13 + v2 *= p1; + ptr += 4; + + v3 += *((uint*)ptr) * p2; + v3 = (v3 << 13) | (v3 >> (32 - 13)); // rotl 13 + v3 *= p1; + ptr += 4; + + v4 += *((uint*)ptr) * p2; + v4 = (v4 << 13) | (v4 >> (32 - 13)); // rotl 13 + v4 *= p1; + ptr += 4; + + } while (ptr <= limit); + + h32 = ((v1 << 1) | (v1 >> (32 - 1))) + // rotl 1 + ((v2 << 7) | (v2 >> (32 - 7))) + // rotl 7 + ((v3 << 12) | (v3 >> (32 - 12))) + // rotl 12 + ((v4 << 18) | (v4 >> (32 - 18))); // rotl 18 + } + else + { + h32 = seed + p5; + } + + h32 += (uint)length; + + // finalize + while (ptr <= end - 4) + { + h32 += *((uint*)ptr) * p3; + h32 = ((h32 << 17) | (h32 >> (32 - 17))) * p4; // (rotl 17) * p4 + ptr += 4; + } + + while (ptr < end) + { + h32 += *((byte*)ptr) * p5; + h32 = ((h32 << 11) | (h32 >> (32 - 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 = (v1 << 13) | (v1 >> (32 - 13)); // rotl 13 + v1 *= p1; + ptr += 4; + + v2 += *((uint*)ptr) * p2; + v2 = (v2 << 13) | (v2 >> (32 - 13)); // rotl 13 + v2 *= p1; + ptr += 4; + + v3 += *((uint*)ptr) * p2; + v3 = (v3 << 13) | (v3 >> (32 - 13)); // rotl 13 + v3 *= p1; + ptr += 4; + + v4 += *((uint*)ptr) * p2; + v4 = (v4 << 13) | (v4 >> (32 - 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 = ((v1 << 1) | (v1 >> (32 - 1))) + // rotl 1 + ((v2 << 7) | (v2 >> (32 - 7))) + // rotl 7 + ((v3 << 12) | (v3 >> (32 - 12))) + // rotl 12 + ((v4 << 18) | (v4 >> (32 - 18))); // rotl 18 + } + else + { + h32 = seed + p5; + } + + h32 += (uint)length; + + // finalize + while (ptr <= end - 4) + { + h32 += *((uint*)ptr) * p3; + h32 = ((h32 << 17) | (h32 >> (32 - 17))) * p4; // (rotl 17) * p4 + ptr += 4; + } + + while (ptr < end) + { + h32 += *((byte*)ptr) * p5; + h32 = ((h32 << 11) | (h32 >> (32 - 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.cs b/src/Standart.Hash.xxHash/xxHash32.cs deleted file mode 100644 index 47153f0..0000000 --- a/src/Standart.Hash.xxHash/xxHash32.cs +++ /dev/null @@ -1,129 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System; - using System.Diagnostics; - 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 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 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) - { - 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 = (v1 << 13) | (v1 >> (32 - 13)); // rotl 13 - v1 *= p1; - ptr += 4; - - v2 += *((uint*)ptr) * p2; - v2 = (v2 << 13) | (v2 >> (32 - 13)); // rotl 13 - v2 *= p1; - ptr += 4; - - v3 += *((uint*)ptr) * p2; - v3 = (v3 << 13) | (v3 >> (32 - 13)); // rotl 13 - v3 *= p1; - ptr += 4; - - v4 += *((uint*)ptr) * p2; - v4 = (v4 << 13) | (v4 >> (32 - 13)); // rotl 13 - v4 *= p1; - ptr += 4; - - } while (ptr <= limit); - - h32 = ((v1 << 1) | (v1 >> (32 - 1))) + // rotl 1 - ((v2 << 7) | (v2 >> (32 - 7))) + // rotl 7 - ((v3 << 12) | (v3 >> (32 - 12))) + // rotl 12 - ((v4 << 18) | (v4 >> (32 - 18))); // rotl 18 - } - else - { - h32 = seed + p5; - } - - h32 += (uint) length; - - // finalize - while (ptr <= end - 4) - { - h32 += *((uint*)ptr) * p3; - h32 = ((h32 << 17) | (h32 >> (32 - 17))) * p4; // (rotl 17) * p4 - ptr += 4; - } - - while (ptr < end) - { - h32 += *((byte*)ptr) * p5; - h32 = ((h32 << 11) | (h32 >> (32 - 11))) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h32 ^= h32 >> 15; - h32 *= p2; - h32 ^= h32 >> 13; - h32 *= p3; - h32 ^= h32 >> 16; - - return h32; - } - } -} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash64.Array.cs b/src/Standart.Hash.xxHash/xxHash64.Array.cs new file mode 100644 index 0000000..888913f --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash64.Array.cs @@ -0,0 +1,26 @@ +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); + } + } + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash64.Async.cs b/src/Standart.Hash.xxHash/xxHash64.Async.cs new file mode 100644 index 0000000..7339b3e --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash64.Async.cs @@ -0,0 +1,68 @@ +namespace Standart.Hash.xxHash +{ + using System.Buffers; + using System.Diagnostics; + using System.IO; + 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) + { + 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)) > 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); + } + } + } +} diff --git a/src/Standart.Hash.xxHash/xxHash64.Span.cs b/src/Standart.Hash.xxHash/xxHash64.Span.cs new file mode 100644 index 0000000..f393a8c --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash64.Span.cs @@ -0,0 +1,27 @@ +namespace Standart.Hash.xxHash +{ + using System; + using System.Diagnostics; + + 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 = &data[0]) + { + return UnsafeComputeHash(pData, length, seed); + } + } + } +} diff --git a/src/Standart.Hash.xxHash/xxHash64.Stream.cs b/src/Standart.Hash.xxHash/xxHash64.Stream.cs index f089143..66d92c8 100644 --- a/src/Standart.Hash.xxHash/xxHash64.Stream.cs +++ b/src/Standart.Hash.xxHash/xxHash64.Stream.cs @@ -3,134 +3,9 @@ using System.Buffers; using System.Diagnostics; using System.IO; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - public static partial class xxHash64 { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void Shift(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 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 - v1 *= p1; - ptr += 8; - - v2 += *((ulong*)ptr) * p2; - v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 - v2 *= p1; - ptr += 8; - - v3 += *((ulong*)ptr) * p2; - v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 - v3 *= p1; - ptr += 8; - - v4 += *((ulong*)ptr) * p2; - v4 = (v4 << 31) | (v4 >> (64 - 31)); // rotl 31 - v4 *= p1; - ptr += 8; - - } while (ptr < limit); - } - } - - [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 >= 32) - { - h64 = ((v1 << 1) | (v1 >> (64 - 1))) + // rotl 1 - ((v2 << 7) | (v2 >> (64 - 7))) + // rotl 7 - ((v3 << 12) | (v3 >> (64 - 12))) + // rotl 12 - ((v4 << 18) | (v4 >> (64 - 18))); // rotl 18 - - // merge round - v1 *= p2; - v1 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 - v1 *= p1; - h64 ^= v1; - h64 = h64 * p1 + p4; - - // merge round - v2 *= p2; - v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 - v2 *= p1; - h64 ^= v2; - h64 = h64 * p1 + p4; - - // merge round - v3 *= p2; - v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 - v3 *= p1; - h64 ^= v3; - h64 = h64 * p1 + p4; - - // merge round - v4 *= p2; - v4 = (v4 << 31) | (v4 >> (64 - 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 = (t1 << 31) | (t1 >> (64 - 31)); // rotl 31 - t1 *= p1; - h64 ^= t1; - h64 = ((h64 << 27) | (h64 >> (64 - 27))) * p1 + p4; // (rotl 27) * p1 + p4 - ptr += 8; - } - - if (ptr <= end - 4) - { - h64 ^= *((uint*)ptr) * p1; - h64 = ((h64 << 23) | (h64 >> (64 - 23))) * p2 + p3; // (rotl 27) * p2 + p3 - ptr += 4; - } - - while (ptr < end) - { - h64 ^= *((byte*)ptr) * p5; - h64 = ((h64 << 11) | (h64 >> (64 - 11))) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h64 ^= h64 >> 33; - h64 *= p2; - h64 ^= h64 >> 29; - h64 *= p3; - h64 ^= h64 >> 32; - - return h64; - } - } - /// /// Compute xxHash for the stream /// @@ -170,7 +45,7 @@ int l = offset - r; // length // Process the next chunk - Shift(buffer, l, ref v1, ref v2, ref v3, ref v4); + UnsafeAlign(buffer, l, ref v1, ref v2, ref v3, ref v4); // Put remaining bytes to buffer UnsafeBuffer.BlockCopy(buffer, l, buffer, 0, r); @@ -178,7 +53,7 @@ } // Process the final chunk - ulong h64 = Final(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); + ulong h64 = UnsafeFinal(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed); return h64; } @@ -188,63 +63,5 @@ ArrayPool.Shared.Return(buffer); } } - - /// - /// 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) - { - 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)) > 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 - Shift(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 = Final(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 new file mode 100644 index 0000000..b5d38bd --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash64.Unsafe.cs @@ -0,0 +1,278 @@ +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 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 + v1 *= p1; + ptr += 8; + + v2 += *((ulong*)ptr) * p2; + v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 + v2 *= p1; + ptr += 8; + + v3 += *((ulong*)ptr) * p2; + v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 + v3 *= p1; + ptr += 8; + + v4 += *((ulong*)ptr) * p2; + v4 = (v4 << 31) | (v4 >> (64 - 31)); // rotl 31 + v4 *= p1; + ptr += 8; + + } while (ptr <= limit); + + h64 = ((v1 << 1) | (v1 >> (64 - 1))) + // rotl 1 + ((v2 << 7) | (v2 >> (64 - 7))) + // rotl 7 + ((v3 << 12) | (v3 >> (64 - 12))) + // rotl 12 + ((v4 << 18) | (v4 >> (64 - 18))); // rotl 18 + + // merge round + v1 *= p2; + v1 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 + v1 *= p1; + h64 ^= v1; + h64 = h64 * p1 + p4; + + // merge round + v2 *= p2; + v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 + v2 *= p1; + h64 ^= v2; + h64 = h64 * p1 + p4; + + // merge round + v3 *= p2; + v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 + v3 *= p1; + h64 ^= v3; + h64 = h64 * p1 + p4; + + // merge round + v4 *= p2; + v4 = (v4 << 31) | (v4 >> (64 - 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 = (t1 << 31) | (t1 >> (64 - 31)); // rotl 31 + t1 *= p1; + h64 ^= t1; + h64 = ((h64 << 27) | (h64 >> (64 - 27))) * p1 + p4; // (rotl 27) * p1 + p4 + ptr += 8; + } + + if (ptr <= end - 4) + { + h64 ^= *((uint*)ptr) * p1; + h64 = ((h64 << 23) | (h64 >> (64 - 23))) * p2 + p3; // (rotl 27) * p2 + p3 + ptr += 4; + } + + while (ptr < end) + { + h64 ^= *((byte*)ptr) * p5; + h64 = ((h64 << 11) | (h64 >> (64 - 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 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 + v1 *= p1; + ptr += 8; + + v2 += *((ulong*)ptr) * p2; + v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 + v2 *= p1; + ptr += 8; + + v3 += *((ulong*)ptr) * p2; + v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 + v3 *= p1; + ptr += 8; + + v4 += *((ulong*)ptr) * p2; + v4 = (v4 << 31) | (v4 >> (64 - 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 = ((v1 << 1) | (v1 >> (64 - 1))) + // rotl 1 + ((v2 << 7) | (v2 >> (64 - 7))) + // rotl 7 + ((v3 << 12) | (v3 >> (64 - 12))) + // rotl 12 + ((v4 << 18) | (v4 >> (64 - 18))); // rotl 18 + + // merge round + v1 *= p2; + v1 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 + v1 *= p1; + h64 ^= v1; + h64 = h64 * p1 + p4; + + // merge round + v2 *= p2; + v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 + v2 *= p1; + h64 ^= v2; + h64 = h64 * p1 + p4; + + // merge round + v3 *= p2; + v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 + v3 *= p1; + h64 ^= v3; + h64 = h64 * p1 + p4; + + // merge round + v4 *= p2; + v4 = (v4 << 31) | (v4 >> (64 - 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 = (t1 << 31) | (t1 >> (64 - 31)); // rotl 31 + t1 *= p1; + h64 ^= t1; + h64 = ((h64 << 27) | (h64 >> (64 - 27))) * p1 + p4; // (rotl 27) * p1 + p4 + ptr += 8; + } + + if (ptr <= end - 4) + { + h64 ^= *((uint*)ptr) * p1; + h64 = ((h64 << 23) | (h64 >> (64 - 23))) * p2 + p3; // (rotl 27) * p2 + p3 + ptr += 4; + } + + while (ptr < end) + { + h64 ^= *((byte*)ptr) * p5; + h64 = ((h64 << 11) | (h64 >> (64 - 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.cs b/src/Standart.Hash.xxHash/xxHash64.cs deleted file mode 100644 index c2ce918..0000000 --- a/src/Standart.Hash.xxHash/xxHash64.cs +++ /dev/null @@ -1,167 +0,0 @@ -namespace Standart.Hash.xxHash -{ - using System; - using System.Diagnostics; - 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 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 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) - { - 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 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 - v1 *= p1; - ptr += 8; - - v2 += *((ulong*)ptr) * p2; - v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 - v2 *= p1; - ptr += 8; - - v3 += *((ulong*)ptr) * p2; - v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 - v3 *= p1; - ptr += 8; - - v4 += *((ulong*)ptr) * p2; - v4 = (v4 << 31) | (v4 >> (64 - 31)); // rotl 31 - v4 *= p1; - ptr += 8; - - } while (ptr <= limit); - - h64 = ((v1 << 1) | (v1 >> (64 - 1))) + // rotl 1 - ((v2 << 7) | (v2 >> (64 - 7))) + // rotl 7 - ((v3 << 12) | (v3 >> (64 - 12))) + // rotl 12 - ((v4 << 18) | (v4 >> (64 - 18))); // rotl 18 - - // merge round - v1 *= p2; - v1 = (v1 << 31) | (v1 >> (64 - 31)); // rotl 31 - v1 *= p1; - h64 ^= v1; - h64 = h64 * p1 + p4; - - // merge round - v2 *= p2; - v2 = (v2 << 31) | (v2 >> (64 - 31)); // rotl 31 - v2 *= p1; - h64 ^= v2; - h64 = h64 * p1 + p4; - - // merge round - v3 *= p2; - v3 = (v3 << 31) | (v3 >> (64 - 31)); // rotl 31 - v3 *= p1; - h64 ^= v3; - h64 = h64 * p1 + p4; - - // merge round - v4 *= p2; - v4 = (v4 << 31) | (v4 >> (64 - 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 = (t1 << 31) | (t1 >> (64 - 31)); // rotl 31 - t1 *= p1; - h64 ^= t1; - h64 = ((h64 << 27) | (h64 >> (64 - 27))) * p1 + p4; // (rotl 27) * p1 + p4 - ptr += 8; - } - - if (ptr <= end - 4) - { - h64 ^= *((uint*)ptr) * p1; - h64 = ((h64 << 23) | (h64 >> (64 - 23))) * p2 + p3; // (rotl 27) * p2 + p3 - ptr += 4; - } - - while (ptr < end) - { - h64 ^= *((byte*)ptr) * p5; - h64 = ((h64 << 11) | (h64 >> (64 - 11))) * p1; // (rotl 11) * p1 - ptr += 1; - } - - // avalanche - h64 ^= h64 >> 33; - h64 *= p2; - h64 ^= h64 >> 29; - h64 *= p3; - h64 ^= h64 >> 32; - - return h64; - } - } -} \ No newline at end of file