Code refactoring
This commit is contained in:
parent
d801b9996b
commit
462293ee0e
@ -6,7 +6,8 @@
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var summary = BenchmarkRunner.Run<xxHashBenchmark>();
|
||||
BenchmarkRunner.Run<xxHashBenchmark>();
|
||||
BenchmarkRunner.Run<UnsafeBufferBenchmark>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
26
src/Standart.Hash.xxHash/xxHash32.Array.cs
Normal file
26
src/Standart.Hash.xxHash/xxHash32.Array.cs
Normal file
@ -0,0 +1,26 @@
|
||||
namespace Standart.Hash.xxHash
|
||||
{
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class xxHash32
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte array
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/Standart.Hash.xxHash/xxHash32.Async.cs
Normal file
68
src/Standart.Hash.xxHash/xxHash32.Async.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute xxHash for the async stream
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream of data</param>
|
||||
/// <param name="bufferSize">The buffer size</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>The hash</returns>
|
||||
public static async ValueTask<uint> ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0)
|
||||
{
|
||||
Debug.Assert(stream != null);
|
||||
Debug.Assert(bufferSize > 16);
|
||||
|
||||
// Optimizing memory allocation
|
||||
byte[] buffer = ArrayPool<byte>.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<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/Standart.Hash.xxHash/xxHash32.Span.cs
Normal file
27
src/Standart.Hash.xxHash/xxHash32.Span.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Standart.Hash.xxHash
|
||||
{
|
||||
public static partial class xxHash32
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte span
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
public static unsafe uint ComputeHash(Span<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the stream
|
||||
/// </summary>
|
||||
@ -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<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the async stream
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream of data</param>
|
||||
/// <param name="bufferSize">The buffer size</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>The hash</returns>
|
||||
public static async ValueTask<uint> ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0)
|
||||
{
|
||||
Debug.Assert(stream != null);
|
||||
Debug.Assert(bufferSize > 16);
|
||||
|
||||
// Optimizing memory allocation
|
||||
byte[] buffer = ArrayPool<byte>.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;
|
||||
}
|
||||
|
||||
200
src/Standart.Hash.xxHash/xxHash32.Unsafe.cs
Normal file
200
src/Standart.Hash.xxHash/xxHash32.Unsafe.cs
Normal file
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxhash32 for the unsafe array of memory
|
||||
/// </summary>
|
||||
/// <param name="ptr"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <param name="seed"></param>
|
||||
/// <returns></returns>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the first part of xxhash32 (need for the streaming api)
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="l"></param>
|
||||
/// <param name="v1"></param>
|
||||
/// <param name="v2"></param>
|
||||
/// <param name="v3"></param>
|
||||
/// <param name="v4"></param>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the second part of xxhash32 (need for the streaming api)
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="l"></param>
|
||||
/// <param name="v1"></param>
|
||||
/// <param name="v2"></param>
|
||||
/// <param name="v3"></param>
|
||||
/// <param name="v4"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <param name="seed"></param>
|
||||
/// <returns></returns>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte array
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte span
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
public static unsafe uint ComputeHash(Span<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);
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/Standart.Hash.xxHash/xxHash64.Array.cs
Normal file
26
src/Standart.Hash.xxHash/xxHash64.Array.cs
Normal file
@ -0,0 +1,26 @@
|
||||
namespace Standart.Hash.xxHash
|
||||
{
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class xxHash64
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte array
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/Standart.Hash.xxHash/xxHash64.Async.cs
Normal file
68
src/Standart.Hash.xxHash/xxHash64.Async.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute xxHash for the async stream
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream of data</param>
|
||||
/// <param name="bufferSize">The buffer size</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>The hash</returns>
|
||||
public static async ValueTask<ulong> ComputeHashAsync(Stream stream, int bufferSize = 8192, ulong seed = 0)
|
||||
{
|
||||
Debug.Assert(stream != null);
|
||||
Debug.Assert(bufferSize > 32);
|
||||
|
||||
// Optimizing memory allocation
|
||||
byte[] buffer = ArrayPool<byte>.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<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/Standart.Hash.xxHash/xxHash64.Span.cs
Normal file
27
src/Standart.Hash.xxHash/xxHash64.Span.cs
Normal file
@ -0,0 +1,27 @@
|
||||
namespace Standart.Hash.xxHash
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class xxHash64
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte span
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
public static unsafe ulong ComputeHash(Span<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the stream
|
||||
/// </summary>
|
||||
@ -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<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the async stream
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream of data</param>
|
||||
/// <param name="bufferSize">The buffer size</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>The hash</returns>
|
||||
public static async ValueTask<ulong> ComputeHashAsync(Stream stream, int bufferSize = 8192, ulong seed = 0)
|
||||
{
|
||||
Debug.Assert(stream != null);
|
||||
Debug.Assert(bufferSize > 32);
|
||||
|
||||
// Optimizing memory allocation
|
||||
byte[] buffer = ArrayPool<byte>.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<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
278
src/Standart.Hash.xxHash/xxHash64.Unsafe.cs
Normal file
278
src/Standart.Hash.xxHash/xxHash64.Unsafe.cs
Normal file
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxhash64 for the unsafe array of memory
|
||||
/// </summary>
|
||||
/// <param name="ptr"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <param name="seed"></param>
|
||||
/// <returns></returns>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the first part of xxhash64 (need for the streaming api)
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="l"></param>
|
||||
/// <param name="v1"></param>
|
||||
/// <param name="v2"></param>
|
||||
/// <param name="v3"></param>
|
||||
/// <param name="v4"></param>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the second part of xxhash64 (need for the streaming api)
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="l"></param>
|
||||
/// <param name="v1"></param>
|
||||
/// <param name="v2"></param>
|
||||
/// <param name="v3"></param>
|
||||
/// <param name="v4"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <param name="seed"></param>
|
||||
/// <returns></returns>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte array
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute xxHash for the data byte span
|
||||
/// </summary>
|
||||
/// <param name="data">The source of data</param>
|
||||
/// <param name="length">The length of the data for hashing</param>
|
||||
/// <param name="seed">The seed number</param>
|
||||
/// <returns>hash</returns>
|
||||
public static unsafe ulong ComputeHash(Span<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);
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user