1 module cachetools.hash;
2 
3 import std.traits;
4 
5 ///
6 /// For classes (and structs with toHash method) we use v.toHash() to compute hash.
7 /// ===============================================================================
8 /// toHash method CAN BE @nogc or not. HashMap 'nogc' properties is inherited from this method.
9 /// toHash method MUST BE @safe or @trusted, as all HashMap code alredy safe.
10 ///
11 /// See also: https://dlang.org/spec/hash-map.html#using_classes_as_key 
12 /// and https://dlang.org/spec/hash-map.html#using_struct_as_key
13 ///
14 bool UseToHashMethod(T)() {
15     return (is(T == class) || (is(T==struct) && __traits(compiles, {
16         T v = T.init; hash_t h = v.toHash();
17     })));
18 }
19 
20 hash_t hash_function(T)(T v) /* @safe @nogc inherited from toHash method */
21 if ( UseToHashMethod!T )
22 {
23     return v.toHash();
24 }
25 
26 hash_t hash_function(T)(in T v) @nogc @trusted
27 if ( !UseToHashMethod!T )
28 {
29     static if ( isNumeric!T ) {
30         enum m = 0x5bd1e995;
31         hash_t h = v;
32         h ^= h >> 13;
33         h *= m;
34         h ^= h >> 15;
35         return h;
36     }
37     else static if ( is(T == string) ) {
38         // // FNV-1a hash
39         // ulong h = 0xcbf29ce484222325;
40         // foreach (const ubyte c; cast(ubyte[]) v)
41         // {
42         //     h ^= c;
43         //     h *= 0x100000001b3;
44         // }
45         // return cast(hash_t)h;
46         import core.internal.hash : bytesHash;
47         return bytesHash(cast(void*)v.ptr, v.length, 0);
48     }
49     else
50     {
51         const(ubyte)[] bytes = (cast(const(ubyte)*)&v)[0 .. T.sizeof];
52         ulong h = 0xcbf29ce484222325;
53         foreach (const ubyte c; bytes)
54         {
55             h ^= c;
56             h *= 0x100000001b3;
57         }
58         return cast(hash_t)h;
59     }
60 }
61 
62 @safe unittest
63 {
64     //assert(hash_function("abc") == cast(hash_t)0xe71fa2190541574b);
65 
66     struct A0 {}
67     assert(!UseToHashMethod!A0);
68 
69     struct A1 {
70         hash_t toHash() const @safe {
71             return 0;
72         }
73     }
74     assert(UseToHashMethod!A1);
75 
76     // class with toHash override - will use toHash
77     class C0 {
78         override hash_t toHash() const @safe {
79             return 0;
80         }
81     }
82     assert(UseToHashMethod!C0);
83     C0 c0 = new C0();
84     assert(c0.toHash() == 0);
85 
86     // class without toHash override - use Object.toHash method
87     class C1 {
88     }
89     assert(UseToHashMethod!C1);
90 }