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