/* * 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. */ /* * MurmurHash3 was written by Austin Appleby, and is placed in the public * domain. The author hereby disclaims copyright to this source code. */ /* * Source: * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp * (Modified to adapt to Guava coding conventions and to use the HashFunction interface) */ package com.google.common.hash; import static com.google.common.primitives.UnsignedBytes.toInt; import com.google.common.primitives.Chars; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; import java.io.Serializable; import java.nio.ByteBuffer; import javax.annotation.Nullable; /** * See http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp * MurmurHash3_x86_32 * * @author Austin Appleby * @author Dimitris Andreou * @author Kurt Alfred Kluever */ final class Murmur3_32HashFunction extends AbstractStreamingHashFunction implements Serializable { private static final int C1 = 0xcc9e2d51; private static final int C2 = 0x1b873593; private final int seed; Murmur3_32HashFunction(int seed) { this.seed = seed; } @Override public int bits() { return 32; } @Override public Hasher newHasher() { return new Murmur3_32Hasher(seed); } @Override public String toString() { return "Hashing.murmur3_32(" + seed + ")"; } @Override public boolean equals(@Nullable Object object) { if (object instanceof Murmur3_32HashFunction) { Murmur3_32HashFunction other = (Murmur3_32HashFunction) object; return seed == other.seed; } return false; } @Override public int hashCode() { return getClass().hashCode() ^ seed; } @Override public HashCode hashInt(int input) { int k1 = mixK1(input); int h1 = mixH1(seed, k1); return fmix(h1, Ints.BYTES); } @Override public HashCode hashLong(long input) { int low = (int) input; int high = (int) (input >>> 32); int k1 = mixK1(low); int h1 = mixH1(seed, k1); k1 = mixK1(high); h1 = mixH1(h1, k1); return fmix(h1, Longs.BYTES); } // TODO(user): Maybe implement #hashBytes instead? @Override public HashCode hashUnencodedChars(CharSequence input) { int h1 = seed; // step through the CharSequence 2 chars at a time for (int i = 1; i < input.length(); i += 2) { int k1 = input.charAt(i - 1) | (input.charAt(i) << 16); k1 = mixK1(k1); h1 = mixH1(h1, k1); } // deal with any remaining characters if ((input.length() & 1) == 1) { int k1 = input.charAt(input.length() - 1); k1 = mixK1(k1); h1 ^= k1; } return fmix(h1, Chars.BYTES * input.length()); } private static int mixK1(int k1) { k1 *= C1; k1 = Integer.rotateLeft(k1, 15); k1 *= C2; return k1; } private static int mixH1(int h1, int k1) { h1 ^= k1; h1 = Integer.rotateLeft(h1, 13); h1 = h1 * 5 + 0xe6546b64; return h1; } // Finalization mix - force all bits of a hash block to avalanche private static HashCode fmix(int h1, int length) { h1 ^= length; h1 ^= h1 >>> 16; h1 *= 0x85ebca6b; h1 ^= h1 >>> 13; h1 *= 0xc2b2ae35; h1 ^= h1 >>> 16; return HashCode.fromInt(h1); } private static final class Murmur3_32Hasher extends AbstractStreamingHasher { private static final int CHUNK_SIZE = 4; private int h1; private int length; Murmur3_32Hasher(int seed) { super(CHUNK_SIZE); this.h1 = seed; this.length = 0; } @Override protected void process(ByteBuffer bb) { int k1 = Murmur3_32HashFunction.mixK1(bb.getInt()); h1 = Murmur3_32HashFunction.mixH1(h1, k1); length += CHUNK_SIZE; } @Override protected void processRemaining(ByteBuffer bb) { length += bb.remaining(); int k1 = 0; for (int i = 0; bb.hasRemaining(); i += 8) { k1 ^= toInt(bb.get()) << i; } h1 ^= Murmur3_32HashFunction.mixK1(k1); } @Override public HashCode makeHash() { return Murmur3_32HashFunction.fmix(h1, length); } } private static final long serialVersionUID = 0L; }