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