using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using Unity.Collections; namespace UnityEngine.Rendering { /// /// Incremental 32-bit FNV-1a hash builder. /// Use this for change detection or other non-cryptographic hashing where /// equally-shaped inputs should produce well-distributed hashes. /// /// /// As a ref struct, instances cannot be stored in fields, boxed, or /// captured by lambdas/async methods; create one, append inputs, then read /// . /// internal ref struct HashFNV1A32 { /// /// FNV prime. /// const uint k_Prime = 16777619; /// /// FNV offset basis. /// const uint k_OffsetBasis = 2166136261; uint m_Hash; /// /// Creates a new hash builder seeded with the FNV offset basis. /// /// An empty ready to receive calls. public static HashFNV1A32 Create() { return new HashFNV1A32 { m_Hash = k_OffsetBasis }; } /// Mixes an into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in int input) { unchecked { m_Hash = (m_Hash ^ (uint)input) * k_Prime; } } /// Mixes a into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in uint input) { unchecked { m_Hash = (m_Hash ^ input) * k_Prime; } } /// Mixes a into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in bool input) { m_Hash = (m_Hash ^ (input ? 1u : 0u)) * k_Prime; } /// Mixes a into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in float input) { unchecked { m_Hash = (m_Hash ^ (uint)input.GetHashCode()) * k_Prime; } } /// Mixes a into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in double input) { unchecked { m_Hash = (m_Hash ^ (uint)input.GetHashCode()) * k_Prime; } } /// Mixes a into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in Vector2 input) { Append(input.x); Append(input.y); } /// Mixes a into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in Vector3 input) { Append(input.x); Append(input.y); Append(input.z); } /// Mixes a into the running hash. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(in Vector4 input) { Append(input.x); Append(input.y); Append(input.z); Append(input.w); } /// Mixes any value-type input into the running hash via its . /// Any value type. /// Value to fold in. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(T input) where T : struct { unchecked { m_Hash = (m_Hash ^ (uint)input.GetHashCode()) * k_Prime; } } /// The current hash value as a signed 32-bit integer. public int value => (int)m_Hash; /// public override int GetHashCode() { return value; } } static class DelegateHashCodeUtils { //Cache to prevent CompilerGeneratedAttribute extraction for known delegate static readonly Lazy> s_MethodHashCodeToSkipTargetHashMap = new(() => new Dictionary(64)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetFuncHashCode(Delegate del) { //Get MethodInfo hash code as the main one to be used var methodHashCode = RuntimeHelpers.GetHashCode(del.Method); //Check if we are dealing with lambda or static delegates and skip target if we are. //Static methods have a null Target. //Lambdas have a CompilerGeneratedAttribute as they are generated by a compiler. //If Lambda have any captured variable Target hashcode will be different each time we re-create lambda. if (!s_MethodHashCodeToSkipTargetHashMap.Value.TryGetValue(methodHashCode, out var skipTarget)) { skipTarget = del.Target == null || ( del.Method.DeclaringType?.IsNestedPrivate == true && Attribute.IsDefined(del.Method.DeclaringType, typeof(CompilerGeneratedAttribute), false) ); s_MethodHashCodeToSkipTargetHashMap.Value[methodHashCode] = skipTarget; } //Combine method info hashcode and target hashcode if needed return skipTarget ? methodHashCode : methodHashCode ^ RuntimeHelpers.GetHashCode(del.Target); } //used for testing internal static int GetTotalCacheCount() => s_MethodHashCodeToSkipTargetHashMap.Value.Count; internal static void ClearCache() => s_MethodHashCodeToSkipTargetHashMap.Value.Clear(); } }