1 /** 2 * Hash functions for arbitrary binary data. 3 * 4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 5 * Authors: Martin Nowak, Walter Bright, https://www.digitalmars.com 6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d, root/_hash.d) 8 * Documentation: https://dlang.org/phobos/dmd_root_hash.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/hash.d 10 */ 11 12 module dmd.root.hash; 13 14 // MurmurHash2 was written by Austin Appleby, and is placed in the public 15 // domain. The author hereby disclaims copyright to this source code. 16 // https://sites.google.com/site/murmurhash/ 17 uint calcHash(scope const(char)[] data) @nogc nothrow pure @safe 18 { 19 return calcHash(cast(const(ubyte)[])data); 20 } 21 22 /// ditto 23 uint calcHash(scope const(ubyte)[] data) @nogc nothrow pure @safe 24 { 25 // 'm' and 'r' are mixing constants generated offline. 26 // They're not really 'magic', they just happen to work well. 27 enum uint m = 0x5bd1e995; 28 enum int r = 24; 29 // Initialize the hash to a 'random' value 30 uint h = cast(uint) data.length; 31 // Mix 4 bytes at a time into the hash 32 while (data.length >= 4) 33 { 34 uint k = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; 35 k *= m; 36 k ^= k >> r; 37 h = (h * m) ^ (k * m); 38 data = data[4..$]; 39 } 40 // Handle the last few bytes of the input array 41 switch (data.length & 3) 42 { 43 case 3: 44 h ^= data[2] << 16; 45 goto case; 46 case 2: 47 h ^= data[1] << 8; 48 goto case; 49 case 1: 50 h ^= data[0]; 51 h *= m; 52 goto default; 53 default: 54 break; 55 } 56 // Do a few final mixes of the hash to ensure the last few 57 // bytes are well-incorporated. 58 h ^= h >> 13; 59 h *= m; 60 h ^= h >> 15; 61 return h; 62 } 63 64 unittest 65 { 66 char[10] data = "0123456789"; 67 assert(calcHash(data[0..$]) == 439_272_720); 68 assert(calcHash(data[1..$]) == 3_704_291_687); 69 assert(calcHash(data[2..$]) == 2_125_368_748); 70 assert(calcHash(data[3..$]) == 3_631_432_225); 71 } 72 73 // combine and mix two words (boost::hash_combine) 74 size_t mixHash(size_t h, size_t k) @nogc nothrow pure @safe 75 { 76 return h ^ (k + 0x9e3779b9 + (h << 6) + (h >> 2)); 77 } 78 79 unittest 80 { 81 // & uint.max because mixHash output is truncated on 32-bit targets 82 assert((mixHash(0xDE00_1540, 0xF571_1A47) & uint.max) == 0x952D_FC10); 83 }