1 /*
2  * Hunt - A redis client library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module hunt.redis.util.MurmurHash;
13 
14 import hunt.redis.util.SafeEncoder;
15 import hunt.redis.util.Hashing;
16 
17 import hunt.Byte;
18 import hunt.io.ByteBuffer;
19 import hunt.io.BufferUtils;
20 import hunt.util.ByteOrder;
21 
22 /**
23  * This is a very fast, non-cryptographic hash suitable for general hash-based lookup. See
24  * http://murmurhash.googlepages.com/ for more details. <br>
25  * <p>
26  * The C version of MurmurHash 2.0 found at that site was ported to Java by Andrzej Bialecki (ab at
27  * getopt org).
28  * </p>
29  */
30 class MurmurHash : Hashing {
31     /**
32    * Hashes bytes in an array.
33    * @param data The bytes to hash.
34    * @param seed The seed for the hash.
35    * @return The 32 bit hash of the bytes in question.
36    */
37     static int hash(const(ubyte)[] data, int seed) {
38         return hash(BufferUtils.toBuffer(cast(byte[])data), seed);
39     }
40 
41     /**
42    * Hashes bytes in part of an array.
43    * @param data The data to hash.
44    * @param offset Where to start munging.
45    * @param length How many bytes to process.
46    * @param seed The seed to start with.
47    * @return The 32-bit hash of the data in question.
48    */
49     static int hash(const(ubyte)[] data, int offset, int length, int seed) {
50         return hash(BufferUtils.toBuffer(cast(byte[])data, offset, length), seed);
51     }
52 
53     /**
54    * Hashes the bytes in a buffer from the current position to the limit.
55    * @param buf The bytes to hash.
56    * @param seed The seed for the hash.
57    * @return The 32 bit murmur hash of the bytes in the buffer.
58    */
59     static int hash(ByteBuffer buf, int seed) {
60         // save byte order for later restoration
61         ByteOrder byteOrder = buf.order();
62         buf.order(ByteOrder.LittleEndian);
63 
64         int m = 0x5bd1e995;
65         int r = 24;
66 
67         int h = seed ^ buf.remaining();
68 
69         int k;
70         while (buf.remaining() >= 4) {
71             k = buf.getInt();
72 
73             k *= m;
74             k ^= k >>> r;
75             k *= m;
76 
77             h *= m;
78             h ^= k;
79         }
80 
81         if (buf.remaining() > 0) {
82             ByteBuffer finish = BufferUtils.allocate(4).order(ByteOrder.LittleEndian);
83             // for big-endian version, use this first:
84             // finish.position(4-buf.remaining());
85             finish.put(buf).rewind();
86             h ^= finish.getInt();
87             h *= m;
88         }
89 
90         h ^= h >>> 13;
91         h *= m;
92         h ^= h >>> 15;
93 
94         buf.order(byteOrder);
95         return h;
96     }
97 
98     static long hash64A(const(ubyte)[] data, int seed) {
99         return hash64A(BufferUtils.toBuffer(cast(byte[])data), seed);
100     }
101 
102     static long hash64A(const(ubyte)[] data, int offset, int length, int seed) {
103         return hash64A(BufferUtils.toBuffer(cast(byte[])data, offset, length), seed);
104     }
105 
106     static long hash64A(ByteBuffer buf, int seed) {
107         ByteOrder byteOrder = buf.order();
108         buf.order(ByteOrder.LittleEndian);
109 
110         long m = 0xc6a4a7935bd1e995L;
111         int r = 47;
112 
113         long h = seed ^ (buf.remaining() * m);
114 
115         long k;
116         while (buf.remaining() >= 8) {
117             k = buf.getLong();
118 
119             k *= m;
120             k ^= k >>> r;
121             k *= m;
122 
123             h ^= k;
124             h *= m;
125         }
126 
127         if (buf.remaining() > 0) {
128             ByteBuffer finish = BufferUtils.allocate(8).order(ByteOrder.LittleEndian);
129             // for big-endian version, do this first:
130             // finish.position(8-buf.remaining());
131             finish.put(buf).rewind();
132             h ^= finish.getLong();
133             h *= m;
134         }
135 
136         h ^= h >>> r;
137         h *= m;
138         h ^= h >>> r;
139 
140         buf.order(byteOrder);
141         return h;
142     }
143 
144     override long hash(const(ubyte)[] key) {
145         return hash64A(key, 0x1234ABCD);
146     }
147 
148     override long hash(string key) {
149         return hash(SafeEncoder.encode(key));
150     }
151 }