yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
9d7d943db
master
1#ifndef SLANG_CORE_HASH_H 2#define SLANG_CORE_HASH_H 3 4#include "slang-math.h" 5#include "slang.h" 6 7#include <ankerl/unordered_dense.h> 8#include <cstring> 9#include <type_traits> 10 11namespace Slang 12{ 13// 14// Types 15// 16 17// A fixed 64bit wide hash on all targets. 18typedef uint64_t HashCode64 ; 19typedef HashCode64 HashCode ; 20// A fixed 32bit wide hash on all targets. 21typedef uint32_t HashCode32 ; 22 23// 24// Some helpers to determine which hash to use for a type 25// 26 27// Forward declare Hash 28template < typename T > 29struct Hash ; 30 31template < typename T ,typename = void > 32constexpr static bool HasSlangHash = false; 33template < typename T > 34constexpr static bool HasSlangHash < 35T , 36std ::enable_if_t < 37std ::is_convertible_v < decltype ((std ::declval < const T &> ()).getHashCode ()),HashCode64 >>> = 38 true; 39 40// Does the hashmap implementation provide a uniform hash for this type. 41template < typename T ,typename = void > 42constexpr static bool HasWyhash = false; 43template < typename T > 44constexpr static bool HasWyhash < T ,typename ankerl ::unordered_dense ::hash < T > ::is_avalanching > = 45 true; 46 47// We want to have an associated type 'is_avalanching = void' iff we have a 48// hash with good uniformity, the two specializations here add that member 49// when appropriate (since we can't declare an associated type with 50// constexpr if or something terse like that) 51template < typename T ,typename = void > 52struct DetectAvalanchingHash 53{ 54}; 55template < typename T > 56struct DetectAvalanchingHash < T ,std ::enable_if_t < HasWyhash < T >>> 57{ 58using is_avalanching = void ; 59}; 60// Have we marked 'getHashCode' as having good uniformity properties. 61template < typename T > 62struct DetectAvalanchingHash < T ,std ::enable_if_t < T ::kHasUniformHash >> 63{ 64using is_avalanching = void ; 65}; 66 67// A helper for hashing according to the bit representation 68template < typename T ,typename U > 69struct BitCastHash :DetectAvalanchingHash < U > 70{ 71 autooperator ()( const T & t ) const 72{ 73// Doesn't discard or invent bits 74static_assert ( sizeof ( T ) == sizeof ( U )); 75// Can we copy bytes to and fro 76static_assert (std:: is_trivially_copyable_v < T > ); 77static_assert (std:: is_trivially_copyable_v < U > ); 78// Because we construct a U to memcpy into 79static_assert (std:: is_trivially_constructible_v < U > ); 80 81U u ; 82memcpy ( & u , & t , sizeof ( T )); 83return Hash < U > {}( u ); 84} 85}; 86 87// 88// Our hashing functor which disptaches to the most appropriate hashing 89// function for the type 90// 91 92template < typename T > 93struct Hash : DetectAvalanchingHash < T > 94{ 95auto operator ()( const T & t ) const 96{ 97// Our preference is for any hash we've defined ourselves 98if constexpr ( HasSlangHash < T > ) 99return t. getHashCode (); 100// Otherwise fall back to any good hash provided by the hashmap 101// library 102else if constexpr (HasWyhash < T > ) 103return ankerl::unordered_dense::hash < T > {}(t); 104// Otherwise fail 105else 106{ 107// !sizeof(T*) is a 'false' which is dependent on T (pending P2593R0) 108static_assert (! sizeof ( T * ), "No hash implementation found for this type" ); 109// This is to avoid the return type being deduced as 'void' and creating further errors. 110return HashCode64 ( 0 ); 111} 112} 113}; 114 115// Specializations for float and double which hash 0 and -0 to distinct values 116template <> 117struct Hash < float > : BitCastHash < float , uint32_t > 118{ 119}; 120template <> 121struct Hash < double > : BitCastHash < double , uint64_t > 122{ 123}; 124 125// 126// Utility functions for using hashes 127// 128 129// A wrapper for Hash<TKey> 130template < typename TKey > 131auto getHashCode( const TKey & key) 132{ 133return Hash < TKey > {}(key); 134} 135 136inline HashCode64 getHashCode ( const char * buffer, std :: size_t len) 137{ 138return ankerl::unordered_dense::detail::wyhash:: hash (buffer, len); 139} 140 141template < typename T > 142HashCode64 hashObjectBytes( const T & t) 143{ 144static_assert( 145std ::has_unique_object_representations_v < T > , 146"This type must have a unique object representation to use hashObjectBytes" ); 147return getHashCode (reinterpret_cast < const char *> ( & t), sizeof (t)); 148} 149 150// Use in a struct to declare a uniform hash which doens't care about the 151// structure of the members. 152#define SLANG_BYTEWISE_HASHABLE \ 153static constexpr bool kHasUniformHash = true; \ 154::Slang::HashCode64 getHashCode() const \ 155{ \ 156return ::Slang::hashObjectBytes(*this); \ 157} 158 159#define SLANG_COMPONENTWISE_HASHABLE_1 \ 160auto getHashCode() const \ 161{ \ 162const auto& [m1] = *this; \ 163return Slang::getHashCode(m1); \ 164} 165 166#define SLANG_COMPONENTWISE_HASHABLE_2 \ 167auto getHashCode() const \ 168{ \ 169const auto& [m1, m2] = *this; \ 170return combineHash(::Slang::getHashCode(m1), ::Slang::getHashCode(m2)); \ 171} 172 173inline HashCode64 combineHash ( HashCode64 h) 174{ 175return h; 176} 177 178inline HashCode32 combineHash ( HashCode32 h) 179{ 180return h; 181} 182 183// A left fold of a mixing operation 184template < typename H1 , typename H2 , typename... Hs > 185auto combineHash ( H1 n, H2 m, Hs... args) 186{ 187// TODO: restrict the types here more, currently we tend to throw 188// unhashed integers in here along with proper hashes of objects. 189static_assert ( std ::is_convertible_v < H1 , HashCode64 > || std::is_convertible_v < H1 , HashCode32 > ); 190static_assert ( std ::is_convertible_v < H2 , HashCode64 > || std::is_convertible_v < H2 , HashCode32 > ); 191return combineHash ((n * 16777619 ) ^ m, args...); 192} 193 194struct Hasher 195{ 196public : 197Hasher () {} 198 199/// Hash the given `value` and combine it into this hash state 200template < typename T > 201void hashValue ( T const & value) 202{ 203// TODO: Eventually, we should replace `getHashCode` 204// with a "hash into" operation that takes the value 205// and a `Hasher`. 206 207m_hashCode = combineHash ( m_hashCode , getHashCode ( value )); 208} 209 210/// Combine the given `hash` code into the hash state. 211/// 212/// Note: users should prefer to use `hashValue` or `hashObject` 213/// when possible, as they may be able to ensure a higher-quality 214/// hash result (e.g., by using more bits to represent the state 215/// during hashing than are used for the final hash code). 216/// 217void addHash ( HashCode hash) { m_hashCode = combineHash (m_hashCode, hash); } 218 219HashCode getResult () const { return m_hashCode; } 220 221private : 222HashCode m_hashCode = 0 ; 223}; 224} // namespace Slang 225 226#endif