summaryrefslogtreecommitdiffstats
path: root/guava/src/com/google/common/hash/Murmur3_128HashFunction.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/hash/Murmur3_128HashFunction.java')
-rw-r--r--guava/src/com/google/common/hash/Murmur3_128HashFunction.java172
1 files changed, 172 insertions, 0 deletions
diff --git a/guava/src/com/google/common/hash/Murmur3_128HashFunction.java b/guava/src/com/google/common/hash/Murmur3_128HashFunction.java
new file mode 100644
index 0000000..c3ec8d8
--- /dev/null
+++ b/guava/src/com/google/common/hash/Murmur3_128HashFunction.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.common.hash;
+
+import static com.google.common.primitives.UnsignedBytes.toInt;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * See http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp
+ * MurmurHash3_x64_128
+ *
+ * @author Austin Appleby
+ * @author Dimitris Andreou
+ */
+final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implements Serializable {
+ // TODO(user): when the shortcuts are implemented, update BloomFilterStrategies
+ private final int seed;
+
+ Murmur3_128HashFunction(int seed) {
+ this.seed = seed;
+ }
+
+ @Override public int bits() {
+ return 128;
+ }
+
+ @Override public Hasher newHasher() {
+ return new Murmur3_128Hasher(seed);
+ }
+
+ private static final class Murmur3_128Hasher extends AbstractStreamingHasher {
+ private static final int CHUNK_SIZE = 16;
+ private static final long C1 = 0x87c37b91114253d5L;
+ private static final long C2 = 0x4cf5ad432745937fL;
+ private long h1;
+ private long h2;
+ private int length;
+
+ Murmur3_128Hasher(int seed) {
+ super(CHUNK_SIZE);
+ this.h1 = seed;
+ this.h2 = seed;
+ this.length = 0;
+ }
+
+ @Override protected void process(ByteBuffer bb) {
+ long k1 = bb.getLong();
+ long k2 = bb.getLong();
+ bmix64(k1, k2);
+ length += CHUNK_SIZE;
+ }
+
+ private void bmix64(long k1, long k2) {
+ h1 ^= mixK1(k1);
+
+ h1 = Long.rotateLeft(h1, 27);
+ h1 += h2;
+ h1 = h1 * 5 + 0x52dce729;
+
+ h2 ^= mixK2(k2);
+
+ h2 = Long.rotateLeft(h2, 31);
+ h2 += h1;
+ h2 = h2 * 5 + 0x38495ab5;
+ }
+
+ @Override protected void processRemaining(ByteBuffer bb) {
+ long k1 = 0;
+ long k2 = 0;
+ length += bb.remaining();
+ switch (bb.remaining()) {
+ case 15:
+ k2 ^= (long) toInt(bb.get(14)) << 48; // fall through
+ case 14:
+ k2 ^= (long) toInt(bb.get(13)) << 40; // fall through
+ case 13:
+ k2 ^= (long) toInt(bb.get(12)) << 32; // fall through
+ case 12:
+ k2 ^= (long) toInt(bb.get(11)) << 24; // fall through
+ case 11:
+ k2 ^= (long) toInt(bb.get(10)) << 16; // fall through
+ case 10:
+ k2 ^= (long) toInt(bb.get(9)) << 8; // fall through
+ case 9:
+ k2 ^= (long) toInt(bb.get(8)); // fall through
+ case 8:
+ k1 ^= bb.getLong();
+ break;
+ case 7:
+ k1 ^= (long) toInt(bb.get(6)) << 48; // fall through
+ case 6:
+ k1 ^= (long) toInt(bb.get(5)) << 40; // fall through
+ case 5:
+ k1 ^= (long) toInt(bb.get(4)) << 32; // fall through
+ case 4:
+ k1 ^= (long) toInt(bb.get(3)) << 24; // fall through
+ case 3:
+ k1 ^= (long) toInt(bb.get(2)) << 16; // fall through
+ case 2:
+ k1 ^= (long) toInt(bb.get(1)) << 8; // fall through
+ case 1:
+ k1 ^= (long) toInt(bb.get(0));
+ break;
+ default:
+ throw new AssertionError("Should never get here.");
+ }
+ h1 ^= mixK1(k1);
+ h2 ^= mixK2(k2);
+ }
+
+ @Override public HashCode makeHash() {
+ h1 ^= length;
+ h2 ^= length;
+
+ h1 += h2;
+ h2 += h1;
+
+ h1 = fmix64(h1);
+ h2 = fmix64(h2);
+
+ h1 += h2;
+ h2 += h1;
+
+ return HashCodes.fromBytesNoCopy(ByteBuffer
+ .wrap(new byte[CHUNK_SIZE])
+ .order(ByteOrder.LITTLE_ENDIAN)
+ .putLong(h1)
+ .putLong(h2)
+ .array());
+ }
+
+ private static long fmix64(long k) {
+ k ^= k >>> 33;
+ k *= 0xff51afd7ed558ccdL;
+ k ^= k >>> 33;
+ k *= 0xc4ceb9fe1a85ec53L;
+ k ^= k >>> 33;
+ return k;
+ }
+
+ private static long mixK1(long k1) {
+ k1 *= C1;
+ k1 = Long.rotateLeft(k1, 31);
+ k1 *= C2;
+ return k1;
+ }
+
+ private static long mixK2(long k2) {
+ k2 *= C2;
+ k2 = Long.rotateLeft(k2, 33);
+ k2 *= C1;
+ return k2;
+ }
+ }
+
+ private static final long serialVersionUID = 0L;
+}