1 /** Various hash functions, including integer ones. 2 */ 3 module nxt.hash_functions; 4 5 @safe pure nothrow: 6 7 /** See_Also: http://forum.dlang.org/post/o1igoc$21ma$1@digitalmars.com 8 * 9 * Doesn't work: integers are returned as is. 10 */ 11 size_t typeidHashOf(T)(in T x) @trusted 12 { 13 pragma(inline, true); 14 return typeid(T).getHash(&x); // TODO why not pure @nogc? 15 } 16 17 /// 18 @safe pure nothrow unittest 19 { 20 // TODO auto x = typeidHashOf(cast(int)17); 21 } 22 23 hash_t hashOfTypeInfoPtr(TypeInfo_Class typeinfo) @trusted pure nothrow @nogc 24 { 25 pragma(inline, true); 26 assert(typeof(typeinfo).alignof == 8); 27 return (cast(hash_t)(cast(void*)typeinfo) >> 3); 28 } 29 30 /** Hash that incorporates the hash of `typeid` bit-xored with `hashOf(a)`. 31 * 32 * See_Also: https://forum.dlang.org/post/lxqoknwuujbymolnlyfw@forum.dlang.org 33 */ 34 hash_t hashOfPolymorphic(Class)(Class a) @trusted pure nothrow @nogc 35 if (is(Class == class)) 36 { 37 pragma(inline, true); 38 static assert(typeid(Class).alignof == 8); 39 // const class_typeid_hash = (cast(hash_t)(cast(void*)typeid(Class)) >> 3) 40 import core.internal.hash : hashOf; 41 return fibonacci_hash(hashOf(cast(void*)typeid(a))) ^ hashOf(a); 42 } 43 44 size_t fibonacci_hash(hash_t hash) @safe pure nothrow @nogc 45 { 46 pragma(inline, true); 47 return (hash * 11400714819323198485LU); 48 } 49 50 version(unittest) 51 { 52 private static: 53 54 class Thing 55 { 56 } 57 58 class Expr : Thing 59 { 60 @safe pure nothrow @nogc: 61 alias Data = string; 62 this(Data data) 63 { 64 this.data = data; 65 } 66 @property override hash_t toHash() const @safe pure nothrow @nogc 67 { 68 return hashOf(data); 69 } 70 Data data; 71 } 72 73 class NounExpr : Expr 74 { 75 @safe pure nothrow @nogc: 76 this(Data data) 77 { 78 super(data); 79 } 80 @property override hash_t toHash() const @safe pure nothrow @nogc 81 { 82 return hashOf(data); 83 } 84 } 85 86 class Year : Thing 87 { 88 @safe pure nothrow @nogc: 89 alias Data = long; 90 @property override hash_t toHash() const @safe pure nothrow @nogc 91 { 92 return hashOf(data); 93 } 94 Data data; 95 } 96 } 97 98 /// 99 @safe pure nothrow @nogc unittest 100 { 101 scope car1 = new Expr("car"); 102 scope car2 = new Expr("car"); 103 scope bar1 = new Expr("bar"); 104 scope ncar = new NounExpr("car"); 105 106 void testEqual() @safe pure nothrow @nogc 107 { 108 assert(hashOf(car1) == hashOf(car2)); 109 assert(hashOfPolymorphic(car1) == hashOfPolymorphic(car2)); 110 } 111 112 void testDifferent1() @safe pure nothrow @nogc 113 { 114 assert(hashOf(car1) != hashOf(bar1)); 115 assert(hashOfPolymorphic(car1) != hashOfPolymorphic(bar1)); 116 } 117 118 void testDifferent2() @safe pure nothrow @nogc 119 { 120 assert(hashOf(car1) == hashOf(ncar)); 121 assert(hashOfPolymorphic(car1) != hashOfPolymorphic(ncar)); 122 } 123 124 testEqual(); 125 testDifferent1(); 126 testDifferent2(); 127 } 128 129 @nogc: 130 131 /** Dummy-hash for benchmarking performance of HashSet. */ 132 ulong identityHash64Of(in ulong x) 133 { 134 pragma(inline, true); 135 return x; // maps -1 to ulong.max 136 } 137 138 /// 139 @safe pure nothrow @nogc unittest 140 { 141 assert(identityHash64Of(-1) == ulong.max); 142 assert(identityHash64Of(int.max) == int.max); 143 assert(identityHash64Of(ulong.max) == ulong.max); 144 } 145 146 /** Mueller integer hash function (bit mixer) A (32-bit). 147 * 148 * See_Also: https://stackoverflow.com/a/12996028/683710 149 * See_Also: http://zimbry.blogspot.se/2011/09/better-bit-mixing-improving-on.html 150 */ 151 uint muellerHash32(uint x) 152 { 153 pragma(inline, true); 154 x = ((x >> 16) ^ x) * 0x45d9f3b; 155 x = ((x >> 16) ^ x) * 0x45d9f3b; 156 x = (x >> 16) ^ x; 157 return x; 158 } 159 160 /** Mueller integer hash function (bit mixer) A (64-bit). 161 * 162 * Based on splitmix64, which seems to be based on the blog article "Better Bit 163 * Mixing" (mix 13). 164 * 165 * See_Also: https://stackoverflow.com/a/12996028/683710 166 * See_Also: http://zimbry.blogspot.se/2011/09/better-bit-mixing-improving-on.html 167 * See_Also: http://xorshift.di.unimi.it/splitmix64.c 168 */ 169 ulong muellerHash64(in ulong x) 170 { 171 pragma(inline, true); 172 typeof(return) y = x; 173 y = (y ^ (y >> 30)) * 0xbf58476d1ce4e5b9UL; 174 y = (y ^ (y >> 27)) * 0x94d049bb133111ebUL; 175 y = y ^ (y >> 31); 176 return y; 177 } 178 179 /** Thomas Wang 64-bit mix integer hash function. 180 * 181 * See_Also: https://gist.github.com/badboy/6267743#64-bit-mix-functions 182 */ 183 public ulong wangMixHash64(ulong x) 184 { 185 pragma(inline, true); 186 x = (~x) + (x << 21); // x = (x << 21) - x - 1; 187 x = x ^ (x >>> 24); 188 x = (x + (x << 3)) + (x << 8); // x * 265 189 x = x ^ (x >>> 14); 190 x = (x + (x << 2)) + (x << 4); // x * 21 191 x = x ^ (x >>> 28); 192 x = x + (x << 31); 193 return x; 194 } 195 196 /// 197 @safe pure nothrow @nogc unittest 198 { 199 assert(wangMixHash64(0) == 8633297058295171728UL); 200 assert(wangMixHash64(1) == 6614235796240398542UL); 201 } 202 203 /** Inspired by lemire's strongly universal hashing. 204 * 205 * See_Also: https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/ 206 * 207 * Instead of shifts, we use rotations so we don't lose any bits. 208 * 209 * Added a final multiplcation with a constant for more mixing. It is most important that the 210 * lower bits are well mixed. 211 */ 212 ulong lemireHash64(in ulong x) 213 { 214 pragma(inline, true); 215 import core.bitop : ror; 216 const ulong h1 = x * 0xA24BAED4963EE407UL; 217 const ulong h2 = ror(x, 32U) * 0x9FB21C651E98DF25UL; 218 const ulong h = ror(h1 + h2, 32U); 219 return h; 220 } 221 222 /// 223 @safe pure nothrow @nogc unittest 224 { 225 assert(lemireHash64(0) == 0UL); 226 assert(lemireHash64(1) == 10826341276197359097UL); 227 assert(lemireHash64(2) == 3205938474390199283UL); 228 } 229 230 /** Inspired by lemire's strongly universal hashing. 231 * 232 * See_Also: https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/ 233 * 234 * Instead of shifts, we use rotations so we don't lose any bits. 235 * 236 * Added a final multiplcation with a constant for more mixing. It is most important that the 237 * lower bits are well mixed. 238 */ 239 ulong lemireHash64(in double x) @trusted 240 { 241 pragma(inline, true); 242 import core.bitop : ror; 243 const ulong h1 = *(cast(ulong*)&x) * 0xA24BAED4963EE407UL; 244 const ulong h2 = ror(*(cast(ulong*)&x), 32U) * 0x9FB21C651E98DF25UL; 245 const ulong h = ror(h1 + h2, 32U); 246 return h; 247 } 248 249 /// 250 @safe pure nothrow @nogc unittest 251 { 252 assert(lemireHash64(0f) == 0UL); 253 assert(lemireHash64(1f) == 5597974336836488763); 254 assert(lemireHash64(2f) == 4611686018555721673UL); 255 }