• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 /*
16  * MurmurHash3 was written by Austin Appleby, and is placed in the public
17  * domain. The author hereby disclaims copyright to this source code.
18  */
19 
20 /*
21  * Source:
22  * https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
23  * (Modified to adapt to Guava coding conventions and to use the HashFunction interface)
24  */
25 
26 package com.google.common.hash;
27 
28 import static com.google.common.primitives.UnsignedBytes.toInt;
29 
30 import com.google.errorprone.annotations.Immutable;
31 import java.io.Serializable;
32 import java.nio.ByteBuffer;
33 import java.nio.ByteOrder;
34 import javax.annotation.CheckForNull;
35 
36 /**
37  * See MurmurHash3_x64_128 in <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp">the
38  * C++ implementation</a>.
39  *
40  * @author Austin Appleby
41  * @author Dimitris Andreou
42  */
43 @Immutable
44 @ElementTypesAreNonnullByDefault
45 final class Murmur3_128HashFunction extends AbstractHashFunction implements Serializable {
46   static final HashFunction MURMUR3_128 = new Murmur3_128HashFunction(0);
47 
48   static final HashFunction GOOD_FAST_HASH_128 =
49       new Murmur3_128HashFunction(Hashing.GOOD_FAST_HASH_SEED);
50 
51   // TODO(user): when the shortcuts are implemented, update BloomFilterStrategies
52   private final int seed;
53 
Murmur3_128HashFunction(int seed)54   Murmur3_128HashFunction(int seed) {
55     this.seed = seed;
56   }
57 
58   @Override
bits()59   public int bits() {
60     return 128;
61   }
62 
63   @Override
newHasher()64   public Hasher newHasher() {
65     return new Murmur3_128Hasher(seed);
66   }
67 
68   @Override
toString()69   public String toString() {
70     return "Hashing.murmur3_128(" + seed + ")";
71   }
72 
73   @Override
equals(@heckForNull Object object)74   public boolean equals(@CheckForNull Object object) {
75     if (object instanceof Murmur3_128HashFunction) {
76       Murmur3_128HashFunction other = (Murmur3_128HashFunction) object;
77       return seed == other.seed;
78     }
79     return false;
80   }
81 
82   @Override
hashCode()83   public int hashCode() {
84     return getClass().hashCode() ^ seed;
85   }
86 
87   private static final class Murmur3_128Hasher extends AbstractStreamingHasher {
88     private static final int CHUNK_SIZE = 16;
89     private static final long C1 = 0x87c37b91114253d5L;
90     private static final long C2 = 0x4cf5ad432745937fL;
91     private long h1;
92     private long h2;
93     private int length;
94 
Murmur3_128Hasher(int seed)95     Murmur3_128Hasher(int seed) {
96       super(CHUNK_SIZE);
97       this.h1 = seed;
98       this.h2 = seed;
99       this.length = 0;
100     }
101 
102     @Override
process(ByteBuffer bb)103     protected void process(ByteBuffer bb) {
104       long k1 = bb.getLong();
105       long k2 = bb.getLong();
106       bmix64(k1, k2);
107       length += CHUNK_SIZE;
108     }
109 
bmix64(long k1, long k2)110     private void bmix64(long k1, long k2) {
111       h1 ^= mixK1(k1);
112 
113       h1 = Long.rotateLeft(h1, 27);
114       h1 += h2;
115       h1 = h1 * 5 + 0x52dce729;
116 
117       h2 ^= mixK2(k2);
118 
119       h2 = Long.rotateLeft(h2, 31);
120       h2 += h1;
121       h2 = h2 * 5 + 0x38495ab5;
122     }
123 
124     @Override
processRemaining(ByteBuffer bb)125     protected void processRemaining(ByteBuffer bb) {
126       long k1 = 0;
127       long k2 = 0;
128       length += bb.remaining();
129       switch (bb.remaining()) {
130         case 15:
131           k2 ^= (long) toInt(bb.get(14)) << 48; // fall through
132         case 14:
133           k2 ^= (long) toInt(bb.get(13)) << 40; // fall through
134         case 13:
135           k2 ^= (long) toInt(bb.get(12)) << 32; // fall through
136         case 12:
137           k2 ^= (long) toInt(bb.get(11)) << 24; // fall through
138         case 11:
139           k2 ^= (long) toInt(bb.get(10)) << 16; // fall through
140         case 10:
141           k2 ^= (long) toInt(bb.get(9)) << 8; // fall through
142         case 9:
143           k2 ^= (long) toInt(bb.get(8)); // fall through
144         case 8:
145           k1 ^= bb.getLong();
146           break;
147         case 7:
148           k1 ^= (long) toInt(bb.get(6)) << 48; // fall through
149         case 6:
150           k1 ^= (long) toInt(bb.get(5)) << 40; // fall through
151         case 5:
152           k1 ^= (long) toInt(bb.get(4)) << 32; // fall through
153         case 4:
154           k1 ^= (long) toInt(bb.get(3)) << 24; // fall through
155         case 3:
156           k1 ^= (long) toInt(bb.get(2)) << 16; // fall through
157         case 2:
158           k1 ^= (long) toInt(bb.get(1)) << 8; // fall through
159         case 1:
160           k1 ^= (long) toInt(bb.get(0));
161           break;
162         default:
163           throw new AssertionError("Should never get here.");
164       }
165       h1 ^= mixK1(k1);
166       h2 ^= mixK2(k2);
167     }
168 
169     @Override
makeHash()170     protected HashCode makeHash() {
171       h1 ^= length;
172       h2 ^= length;
173 
174       h1 += h2;
175       h2 += h1;
176 
177       h1 = fmix64(h1);
178       h2 = fmix64(h2);
179 
180       h1 += h2;
181       h2 += h1;
182 
183       return HashCode.fromBytesNoCopy(
184           ByteBuffer.wrap(new byte[CHUNK_SIZE])
185               .order(ByteOrder.LITTLE_ENDIAN)
186               .putLong(h1)
187               .putLong(h2)
188               .array());
189     }
190 
fmix64(long k)191     private static long fmix64(long k) {
192       k ^= k >>> 33;
193       k *= 0xff51afd7ed558ccdL;
194       k ^= k >>> 33;
195       k *= 0xc4ceb9fe1a85ec53L;
196       k ^= k >>> 33;
197       return k;
198     }
199 
mixK1(long k1)200     private static long mixK1(long k1) {
201       k1 *= C1;
202       k1 = Long.rotateLeft(k1, 31);
203       k1 *= C2;
204       return k1;
205     }
206 
mixK2(long k2)207     private static long mixK2(long k2) {
208       k2 *= C2;
209       k2 = Long.rotateLeft(k2, 33);
210       k2 *= C1;
211       return k2;
212     }
213   }
214 
215   private static final long serialVersionUID = 0L;
216 }
217