1 module ikod.containers.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 public 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 public hash_t hash_function(T)(in T v) @nogc @trusted 27 if ( !UseToHashMethod!T ) 28 { 29 static if (is(T==byte) || is(T==ubyte)) 30 { 31 return v; 32 } 33 else static if ( isNumeric!T ) { 34 enum m = 0x5bd1e995; 35 hash_t h = cast(hash_t)v; 36 h ^= h >> 13; 37 h *= m; 38 h ^= h >> 15; 39 return h; 40 } 41 else static if ( is(T == string) ) { 42 // // FNV-1a hash 43 // ulong h = 0xcbf29ce484222325; 44 // foreach (const ubyte c; cast(ubyte[]) v) 45 // { 46 // h ^= c; 47 // h *= 0x100000001b3; 48 // } 49 // return cast(hash_t)h; 50 import core.internal.hash : bytesHash; 51 return bytesHash(cast(void*)v.ptr, v.length, 0); 52 } 53 else 54 { 55 const(ubyte)[] bytes = (cast(const(ubyte)*)&v)[0 .. T.sizeof]; 56 ulong h = 0xcbf29ce484222325; 57 foreach (const ubyte c; bytes) 58 { 59 h ^= c; 60 h *= 0x100000001b3; 61 } 62 return cast(hash_t)h; 63 } 64 } 65 66 @safe unittest 67 { 68 //assert(hash_function("abc") == cast(hash_t)0xe71fa2190541574b); 69 70 struct A0 {} 71 assert(!UseToHashMethod!A0); 72 73 struct A1 { 74 hash_t toHash() const @safe { 75 return 0; 76 } 77 } 78 assert(UseToHashMethod!A1); 79 80 // class with toHash override - will use toHash 81 class C0 { 82 override hash_t toHash() const @safe { 83 return 0; 84 } 85 } 86 assert(UseToHashMethod!C0); 87 C0 c0 = new C0(); 88 assert(c0.toHash() == 0); 89 90 // class without toHash override - use Object.toHash method 91 class C1 { 92 } 93 assert(UseToHashMethod!C1); 94 }