// ReSharper disable InconsistentNaming using System; using System.Buffers; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace Standart.Hash.xxHash; public static partial class xxHash32 { /// /// Compute xxHash for the data byte array /// /// The source of data /// The length of the data for hashing /// The seed number /// hash public static unsafe uint ComputeHash(byte[] data, int length, uint seed = 0) { Debug.Assert(data != null); Debug.Assert(length >= 0); Debug.Assert(length <= data.Length); fixed (byte* pData = &data[0]) { return UnsafeComputeHash(pData, length, seed); } } /// /// Compute xxHash for the data byte array /// /// The source of data /// The offset of the data for hashing /// The length of the data for hashing /// The seed number /// hash public static unsafe uint ComputeHash(byte[] data, int offset, int length, uint seed = 0) { Debug.Assert(data != null); Debug.Assert(length >= 0); Debug.Assert(offset < data.Length); Debug.Assert(length <= data.Length - offset); fixed (byte* pData = &data[0 + offset]) { return UnsafeComputeHash(pData, length, seed); } } /// /// Compute xxHash for the data byte array /// /// The source of data /// The seed number /// hash public static ulong ComputeHash(ArraySegment data, uint seed = 0) { Debug.Assert(data != null); return ComputeHash(data.Array, data.Offset, data.Count, seed); } /// /// Compute xxHash for the async stream /// /// The stream of data /// The buffer size /// The seed number /// The hash public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize = 4096, uint seed = 0) { return await ComputeHashAsync(stream, bufferSize, seed, CancellationToken.None); } /// /// Compute xxHash for the async stream /// /// The stream of data /// The buffer size /// The seed number /// The cancellation token /// The hash public static async ValueTask ComputeHashAsync(Stream stream, int bufferSize, uint seed, CancellationToken cancellationToken) { Debug.Assert(stream != null); Debug.Assert(bufferSize > 16); // Optimizing memory allocation byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16); int readBytes; int offset = 0; long length = 0; // Prepare the seed vector uint v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; uint v2 = seed + XXH_PRIME32_2; uint v3 = seed + 0; uint v4 = seed - XXH_PRIME32_1; try { // Read flow of bytes while ((readBytes = await stream.ReadAsync(buffer, offset, bufferSize, cancellationToken).ConfigureAwait(false)) > 0) { length = length + readBytes; offset = offset + readBytes; if (offset < 16) continue; int r = offset % 16; // remain int l = offset - r; // length // Process the next chunk __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); } } /// /// Compute xxHash for the data byte span /// /// The source of data /// The length of the data for hashing /// The seed number /// hash public static unsafe uint ComputeHash(Span data, int length, uint seed = 0) { Debug.Assert(data != null); Debug.Assert(length >= 0); Debug.Assert(length <= data.Length); fixed (byte* pData = &MemoryMarshal.GetReference(data)) { return UnsafeComputeHash(pData, length, seed); } } /// /// Compute xxHash for the data byte span /// /// The source of data /// The length of the data for hashing /// The seed number /// hash public static unsafe uint ComputeHash(ReadOnlySpan data, int length, uint seed = 0) { Debug.Assert(data != null); Debug.Assert(length >= 0); Debug.Assert(length <= data.Length); fixed (byte* pData = &MemoryMarshal.GetReference(data)) { return UnsafeComputeHash(pData, length, seed); } } /// /// Compute xxHash for the stream /// /// The stream of data /// The buffer size /// The seed number /// The hash public static uint ComputeHash(Stream stream, int bufferSize = 4096, uint seed = 0) { Debug.Assert(stream != null); Debug.Assert(bufferSize > 16); // Optimizing memory allocation byte[] buffer = ArrayPool.Shared.Rent(bufferSize + 16); int readBytes; int offset = 0; long length = 0; // Prepare the seed vector uint v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; uint v2 = seed + XXH_PRIME32_2; uint v3 = seed + 0; uint v4 = seed - XXH_PRIME32_1; try { // Read flow of bytes while ((readBytes = stream.Read(buffer, offset, bufferSize)) > 0) { length = length + readBytes; offset = offset + readBytes; if (offset < 16) continue; int r = offset % 16; // remain int l = offset - r; // length // Process the next chunk __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); } }