using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
#if NET6_0_OR_GREATER
using System.Runtime.InteropServices;
#endif
using System.Threading;
using System.Threading.Tasks;
namespace DTLib.XXHash
{
public static partial class xxHash32
{
///
/// Compute xxHash for the data byte array
///
/// The source of data
/// The length of the data for hashing
/// The seed number
/// hash
public static unsafe uint ComputeHash(byte[] data, int length, uint seed = 0)
{
Debug.Assert(data != null);
Debug.Assert(length >= 0);
Debug.Assert(length <= data.Length);
fixed (byte* pData = &data[0])
{
return UnsafeComputeHash(pData, length, seed);
}
}
///
/// Compute xxHash for the data byte array
///
/// The source of data
/// The offset of the data for hashing
/// The length of the data for hashing
/// The seed number
/// hash
public static unsafe uint ComputeHash(byte[] data, int offset, int length, uint seed = 0)
{
Debug.Assert(data != null);
Debug.Assert(length >= 0);
Debug.Assert(offset < data.Length);
Debug.Assert(length <= data.Length - offset);
fixed (byte* pData = &data[0 + offset])
{
return UnsafeComputeHash(pData, length, seed);
}
}
///
/// Compute xxHash for the data byte array
///
/// The source of data
/// The seed number
/// hash
public static ulong ComputeHash(ArraySegment data, uint seed = 0)
{
return ComputeHash(data.Array, data.Offset, data.Count, seed);
}
///
/// Compute xxHash for the async stream
///
/// The stream of data
/// The buffer size
/// The seed number
/// The hash
public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0)
{
return await ComputeHashAsync(stream, bufferSize, seed, CancellationToken.None);
}
///
/// Compute xxHash for the async stream
///
/// The stream of data
/// The buffer size
/// The seed number
/// The cancellation token
/// The hash
public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize, uint seed, CancellationToken cancellationToken)
{
Debug.Assert(stream != null);
Debug.Assert(bufferSize > 16);
// Optimizing memory allocation
byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16);
int readBytes;
int offset = 0;
long length = 0;
// Prepare the seed vector
uint v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
uint v2 = seed + XXH_PRIME32_2;
uint v3 = seed + 0;
uint v4 = seed - XXH_PRIME32_1;
try
{
// Read flow of bytes
while ((readBytes =
await stream.ReadAsync(buffer, offset, bufferSize, cancellationToken).ConfigureAwait(false)) > 0)
{
length = length + readBytes;
offset = offset + readBytes;
if (offset < 16) continue;
int r = offset % 16; // remain
int l = offset - r; // length
// Process the next chunk
__inline__XXH32_stream_process(buffer, l, ref v1, ref v2, ref v3, ref v4);
// Put remaining bytes to buffer
Utils.BlockCopy(buffer, l, buffer, 0, r);
offset = r;
}
// Process the final chunk
uint h32 = __inline__XXH32_stream_finalize(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed);
return h32;
}
finally
{
// Free memory
ArrayPool.Shared.Return(buffer);
}
}
#if NET6_0_OR_GREATER
///
/// Compute xxHash for the data byte span
///
/// The source of data
/// The length of the data for hashing
/// The seed number
/// hash
public static unsafe uint ComputeHash(Span data, int length, uint seed = 0)
{
Debug.Assert(data != null);
Debug.Assert(length >= 0);
Debug.Assert(length <= data.Length);
fixed (byte* pData = &MemoryMarshal.GetReference(data))
{
return UnsafeComputeHash(pData, length, seed);
}
}
///
/// Compute xxHash for the data byte span
///
/// The source of data
/// The length of the data for hashing
/// The seed number
/// hash
public static unsafe uint ComputeHash(ReadOnlySpan data, int length, uint seed = 0)
{
Debug.Assert(data != null);
Debug.Assert(length >= 0);
Debug.Assert(length <= data.Length);
fixed (byte* pData = &MemoryMarshal.GetReference(data))
{
return UnsafeComputeHash(pData, length, seed);
}
}
#endif
///
/// Compute xxHash for the stream
///
/// The stream of data
/// The buffer size
/// The seed number
/// The hash
public static uint ComputeHash(Stream stream, int bufferSize = 4096, uint seed = 0)
{
Debug.Assert(stream != null);
Debug.Assert(bufferSize > 16);
// Optimizing memory allocation
byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16);
int readBytes;
int offset = 0;
long length = 0;
// Prepare the seed vector
uint v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
uint v2 = seed + XXH_PRIME32_2;
uint v3 = seed + 0;
uint v4 = seed - XXH_PRIME32_1;
try
{
// Read flow of bytes
while ((readBytes = stream.Read(buffer, offset, bufferSize)) > 0)
{
length = length + readBytes;
offset = offset + readBytes;
if (offset < 16) continue;
int r = offset % 16; // remain
int l = offset - r; // length
// Process the next chunk
__inline__XXH32_stream_process(buffer, l, ref v1, ref v2, ref v3, ref v4);
// Put remaining bytes to buffer
Utils.BlockCopy(buffer, l, buffer, 0, r);
offset = r;
}
// Process the last chunk
uint h32 = __inline__XXH32_stream_finalize(buffer, offset, ref v1, ref v2, ref v3, ref v4, length, seed);
return h32;
}
finally
{
// Free memory
ArrayPool.Shared.Return(buffer);
}
}
///
/// Compute xxHash for the string
///
/// The source of data
/// The seed number
/// hash
public static unsafe uint ComputeHash(string str, uint seed = 0)
{
Debug.Assert(str != null);
fixed (char* c = str)
{
byte* ptr = (byte*) c;
int length = str.Length * 2;
return UnsafeComputeHash(ptr, length, seed);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe uint UnsafeComputeHash(byte* ptr, int length, uint seed)
{
// Use inlined version
// return XXH32(ptr, length, seed);
return __inline__XXH32(ptr, length, seed);
}
}
}