1 /** Various hash functions, including integer ones.
2  */
3 module nxt.hash_functions;
4 
5 import std.traits : isIntegral;
6 
7 pragma(inline, true)
8 @safe nothrow:
9 
10 /** See_Also: http://forum.dlang.org/post/o1igoc$21ma$1@digitalmars.com
11  *
12  * Doesn't work: integers are returned as is.
13  */
14 size_t typeidHashOf(T)(in T x) @trusted
15 {
16     return typeid(T).getHash(&x); // TODO why not pure @nogc?
17 }
18 
19 unittest
20 {
21     // TODO auto x = typeidHashOf(cast(int)17);
22 }
23 
24 hash_t hashOfTypeInfoPtr(TypeInfo_Class typeinfo) @trusted pure nothrow @nogc
25 {
26     assert(typeof(typeinfo).alignof == 8);
27     return (cast(hash_t)(cast(void*)typeinfo) >> 3);
28 }
29 
30 size_t fibonacci_hash(hash_t hash) @safe pure nothrow @nogc
31 {
32     return (hash * 11400714819323198485LU);
33 }
34 
35 /** Hash that incorporates the hash of `typeid` bit-xored with `hashOf(a)`.
36  *
37  * See_Also: https://forum.dlang.org/post/lxqoknwuujbymolnlyfw@forum.dlang.org
38  */
39 hash_t hashOfPolymorphic(Class)(Class a) @trusted pure nothrow @nogc
40 if (is(Class == class))
41 {
42     static assert(typeid(Class).alignof == 8);
43     // const class_typeid_hash = (cast(hash_t)(cast(void*)typeid(Class)) >> 3)
44     import core.internal.hash : hashOf;
45     return fibonacci_hash(hashOf(cast(void*)typeid(a))) ^ hashOf(a);
46 }
47 
48 version(unittest)
49 {
50     private static:
51 
52     class Thing
53     {
54     }
55 
56     class Expr : Thing
57     {
58         @safe pure nothrow @nogc:
59         alias Data = string;
60         this(Data data)
61         {
62             this.data = data;
63         }
64         @property override hash_t toHash() const @safe pure nothrow @nogc
65         {
66             return hashOf(data);
67         }
68         Data data;
69     }
70 
71     class NounExpr : Expr
72     {
73         @safe pure nothrow @nogc:
74         this(Data data)
75         {
76             super(data);
77         }
78         @property override hash_t toHash() const @safe pure nothrow @nogc
79         {
80             return hashOf(data);
81         }
82     }
83 
84     class Year : Thing
85     {
86         @safe pure nothrow @nogc:
87         alias Data = long;
88         @property override hash_t toHash() const @safe pure nothrow @nogc
89         {
90             return hashOf(data);
91         }
92         Data data;
93     }
94 }
95 
96 @safe pure nothrow unittest
97 {
98     auto car1 = new Expr("car");
99     auto car2 = new Expr("car");
100     auto bar1 = new Expr("bar");
101     auto ncar = new NounExpr("car");
102 
103     void testEqual() @safe pure nothrow @nogc
104     {
105         assert(hashOf(car1) == hashOf(car2));
106         assert(hashOfPolymorphic(car1) == hashOfPolymorphic(car2));
107     }
108 
109     void testDifferent1() @safe pure nothrow @nogc
110     {
111         assert(hashOf(car1) != hashOf(bar1));
112         assert(hashOfPolymorphic(car1) != hashOfPolymorphic(bar1));
113     }
114 
115     void testDifferent2() @safe pure nothrow @nogc
116     {
117         assert(hashOf(car1) == hashOf(ncar));
118         assert(hashOfPolymorphic(car1) != hashOfPolymorphic(ncar));
119     }
120 
121     testEqual();
122     testDifferent1();
123     testDifferent2();
124 }
125 
126 pure @nogc:
127 
128 /** Dummy-hash for benchmarking performance of HashSet. */
129 ulong identityHash64Of(T)(in T x)
130 if (isIntegral!T &&
131     T.sizeof <= ulong.sizeof)
132 {
133     return x;               // maps -1 to ulong.max
134 }
135 
136 unittest
137 {
138     assert(identityHash64Of(-1) == ulong.max);
139     assert(identityHash64Of(int.max) == int.max);
140     assert(identityHash64Of(ulong.max) == ulong.max);
141 }
142 
143 /** Mueller integer hash function (bit mixer) A (32-bit).
144  *
145  * See_Also: https://stackoverflow.com/a/12996028/683710
146  * See_Also: http://zimbry.blogspot.se/2011/09/better-bit-mixing-improving-on.html
147  */
148 uint muellerHash32(uint x)
149 {
150     x = ((x >> 16) ^ x) * 0x45d9f3b;
151     x = ((x >> 16) ^ x) * 0x45d9f3b;
152     x = (x >> 16) ^ x;
153     return x;
154 }
155 
156 /** Mueller integer hash function (bit mixer) A (64-bit).
157  *
158  * Based on splitmix64, which seems to be based on the blog article "Better Bit
159  * Mixing" (mix 13).
160  *
161  * See_Also: https://stackoverflow.com/a/12996028/683710
162  * See_Also: http://zimbry.blogspot.se/2011/09/better-bit-mixing-improving-on.html
163  * See_Also: http://xorshift.di.unimi.it/splitmix64.c
164  */
165 ulong muellerHash64(T)(T x)
166 if (isIntegral!T &&
167     T.sizeof <= ulong.sizeof)
168 {
169     typeof(return) y = x;
170     y = (y ^ (y >> 30)) * 0xbf58476d1ce4e5b9UL;
171     y = (y ^ (y >> 27)) * 0x94d049bb133111ebUL;
172     y = y ^ (y >> 31);
173     return y;
174 }
175 
176 /** Thomas Wang 64-bit mix integer hash function.
177  *
178  * See_Also: https://gist.github.com/badboy/6267743#64-bit-mix-functions
179  */
180 public ulong wangMixHash64(ulong x)
181 {
182     x = (~x) + (x << 21); // x = (x << 21) - x - 1;
183     x = x ^ (x >>> 24);
184     x = (x + (x << 3)) + (x << 8); // x * 265
185     x = x ^ (x >>> 14);
186     x = (x + (x << 2)) + (x << 4); // x * 21
187     x = x ^ (x >>> 28);
188     x = x + (x << 31);
189     return x;
190 }
191 
192 unittest
193 {
194     assert(wangMixHash64(0) == 8633297058295171728UL);
195     assert(wangMixHash64(1) == 6614235796240398542UL);
196 }