1 /* 2 * Copyright (c) 2012-2020 The ANTLR Project. All rights reserved. 3 * Use of this file is governed by the BSD 3-clause license that 4 * can be found in the LICENSE.txt file in the project root. 5 */ 6 7 module antlr.v4.runtime.misc.MurmurHash; 8 9 import std.conv; 10 import std.stdio; 11 12 /** 13 * @author Sam Harwell 14 * @author Egbert Voigt (D) 15 */ 16 class MurmurHash 17 { 18 19 enum size_t DEFAULT_SEED = 0; 20 21 /** 22 * Initialize the hash using the default seed value. 23 * 24 * @return the intermediate hash value 25 * @uml 26 * @safe 27 * @nothrow 28 */ 29 public static size_t initialize() @safe nothrow 30 { 31 auto mh = new MurmurHash; 32 return initialize(DEFAULT_SEED); 33 } 34 35 /** 36 * Initialize the hash using the specified {@code seed}. 37 * 38 * @param seed the seed 39 * @return the intermediate hash value 40 * @uml 41 * @safe 42 * @nothrow 43 */ 44 public static size_t initialize(size_t seed) @safe nothrow 45 { 46 return seed; 47 } 48 49 /** 50 * Update the intermediate hash value for the next input {@code value}. 51 * 52 * @param hash the intermediate hash value 53 * @param value the value to add to the current hash 54 * @return the updated intermediate hash value 55 * 56 * @uml 57 * @safe 58 * @nothrow 59 */ 60 public static size_t update(size_t hash, size_t value) @safe nothrow 61 { 62 immutable size_t c1 = 0xCC9E2D51; 63 immutable size_t c2 = 0x1B873593; 64 immutable size_t r1 = 15; 65 immutable size_t r2 = 13; 66 immutable size_t m = 5; 67 immutable size_t n = 0xE6546B64; 68 69 size_t k = value; 70 k = k * c1; 71 k = (k << r1) | (k >>> (32 - r1)); 72 k = k * c2; 73 74 hash = hash ^ k; 75 hash = (hash << r2) | (hash >>> (32 - r2)); 76 hash = hash * m + n; 77 78 return hash; 79 80 } 81 82 /** 83 * Update the intermediate hash value for the next input {@code value}. 84 * 85 * @param hash the intermediate hash value 86 * @param value the value to add to the current hash 87 * @return the updated intermediate hash value 88 * 89 * @uml 90 * @nothrow 91 */ 92 public static size_t update(U)(size_t hash, U value) nothrow 93 { 94 return update(hash, value !is null ? value.toHash : 0); 95 } 96 97 /** 98 * Apply the final computation steps to the intermediate value {@code hash} 99 * to form the final result of the MurmurHash 3 hash function. 100 * 101 * @param hash the intermediate hash value 102 * @param numberOfWords the number of integer values added to the hash 103 * @return the final hash result 104 * @uml 105 * @safe 106 * @nothrow 107 */ 108 public static size_t finish(size_t hash, size_t numberOfWords) @safe nothrow 109 { 110 hash = hash ^ (cast(size_t) numberOfWords * 4); 111 hash = hash ^ (hash >>> 16); 112 hash = hash * 0x85EBCA6B; 113 hash = hash ^ (hash >>> 13); 114 hash = hash * 0xC2B2AE35; 115 hash = hash ^ (hash >>> 16); 116 return hash; 117 } 118 119 /** 120 * Utility function to compute the hash code of an array using the 121 * MurmurHash algorithm. 122 * 123 * @param T the array element type 124 * @param data the array data 125 * @param seed the seed for the MurmurHash algorithm 126 * @return the hash code of the data 127 */ 128 public static size_t hashCode(T)(T[] data, size_t seed) 129 { 130 size_t hash = initialize(seed); 131 foreach (T value; data) 132 { 133 hash = update(hash, value); 134 } 135 hash = finish(hash, data.length); 136 return hash; 137 } 138 139 } 140 141 version (unittest) 142 { 143 import dshould : equal, should; 144 import unit_threaded; 145 146 @Tags("MurmurHash") 147 @("Calculation") 148 unittest 149 { 150 auto hash = MurmurHash.initialize; 151 hash.should.equal(0); 152 hash = MurmurHash.update(hash, 33); 153 static if (size_t.sizeof == 4) 154 { 155 hash.should.equal(3641358107U); 156 } 157 else 158 { 159 hash.should.equal(6137767987951124103UL); 160 } 161 hash = MurmurHash.finish(hash, 1); 162 static if (size_t.sizeof == 4) 163 { 164 hash.should.equal(2689861987U); 165 } 166 else 167 { 168 hash.should.equal(4470425249505779227UL); 169 } 170 } 171 }