// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CommunityToolkit.HighPerformance.Helpers.Internals; #if NETSTANDARD2_1_OR_GREATER using RuntimeHelpers = System.Runtime.CompilerServices.RuntimeHelpers; #else using RuntimeHelpers = CommunityToolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; #endif namespace CommunityToolkit.HighPerformance.Helpers; /// /// Combines the hash code of sequences of values into a single hash code. /// /// The type of values to hash. /// /// The hash codes returned by the method are only guaranteed to be repeatable for /// the current execution session, just like with the available APIs.In other words, /// hashing the same collection multiple times in the same process will always /// result in the same hash code, while the same collection being hashed again from another process /// (or another instance of the same process) is not guaranteed to result in the same final value. /// For more info, see . /// public struct HashCode where T : notnull { /// /// Gets a content hash from the input instance using the xxHash32 algorithm. /// /// The input instance /// The xxHash32 value for the input instance /// The xxHash32 is only guaranteed to be deterministic within the scope of a single app execution [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Combine(ReadOnlySpan span) { int hash = CombineValues(span); return HashCode.Combine(hash); } /// /// Gets a content hash from the input instance. /// /// The input instance /// The hash code for the input instance /// The returned hash code is not processed through APIs. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int CombineValues(ReadOnlySpan span) { ref T r0 = ref MemoryMarshal.GetReference(span); // If typeof(T) is not unmanaged, iterate over all the items one by one. // This check is always known in advance either by the JITter or by the AOT // compiler, so this branch will never actually be executed by the code. if (RuntimeHelpers.IsReferenceOrContainsReferences()) { return SpanHelper.GetDjb2HashCode(ref r0, (nint)(uint)span.Length); } // Get the info for the target memory area to process. // The line below is computing the total byte size for the span, // and we cast both input factors to uint first to avoid sign extensions // (they're both guaranteed to always be positive values), and to let the // JIT avoid the 64 bit computation entirely when running in a 32 bit // process. In that case it will just compute the byte size as a 32 bit // multiplication with overflow, which is guaranteed never to happen anyway. ref byte rb = ref Unsafe.As(ref r0); nint length = (nint)((uint)span.Length * (uint)Unsafe.SizeOf()); return SpanHelper.GetDjb2LikeByteHash(ref rb, length); } }