// Copyright (c) 2003-present, Jodd Team (http://jodd.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. package jodd.util; import java.io.UnsupportedEncodingException; /** * MurmurHash3 128bits. * Adaptation of http://github.com/yonik/java_util for future updates to this file. */ public class MurmurHash3 { /** * 128 bits of state. */ public static final class HashValue { public final long val1; public final long val2; public HashValue(long val1, long val2) { this.val1 = val1; this.val2 = val2; } public String toString() { return Long.toHexString(val1) + Long.toHexString(val2); } } public static long fmix64(long k) { k ^= k >>> 33; k *= 0xff51afd7ed558ccdL; k ^= k >>> 33; k *= 0xc4ceb9fe1a85ec53L; k ^= k >>> 33; return k; } /** * Gets a long from a byte buffer in little endian byte order. */ public static long getLongLittleEndian(byte[] buf, int offset) { return ((long) buf[offset + 7] << 56) // no mask needed | ((buf[offset + 6] & 0xffL) << 48) | ((buf[offset + 5] & 0xffL) << 40) | ((buf[offset + 4] & 0xffL) << 32) | ((buf[offset + 3] & 0xffL) << 24) | ((buf[offset + 2] & 0xffL) << 16) | ((buf[offset + 1] & 0xffL) << 8) | ((buf[offset] & 0xffL)); // no shift needed } public static HashValue murmurhash3_x64_128(String str, int seed) { try { byte[] bytes = str.getBytes("UTF-8"); return murmurhash3_x64_128(bytes, 0, bytes.length, seed); } catch (UnsupportedEncodingException ignore) { return null; } } /** * Returns the MurmurHash3_x64_128 hash. */ public static HashValue murmurhash3_x64_128(byte[] key, int offset, int len, int seed) { // The original algorithm does have a 32 bit unsigned seed. // We have to mask to match the behavior of the unsigned types and prevent sign extension. long h1 = seed & 0x00000000FFFFFFFFL; long h2 = seed & 0x00000000FFFFFFFFL; final long c1 = 0x87c37b91114253d5L; final long c2 = 0x4cf5ad432745937fL; int roundedEnd = offset + (len & 0xFFFFFFF0); // round down to 16 byte block for (int i = offset; i < roundedEnd; i += 16) { long k1 = getLongLittleEndian(key, i); long k2 = getLongLittleEndian(key, i + 8); k1 *= c1; k1 = Long.rotateLeft(k1, 31); k1 *= c2; h1 ^= k1; h1 = Long.rotateLeft(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; k2 *= c2; k2 = Long.rotateLeft(k2, 33); k2 *= c1; h2 ^= k2; h2 = Long.rotateLeft(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } long k1 = 0; long k2 = 0; switch (len & 15) { case 15: k2 = (key[roundedEnd + 14] & 0xffL) << 48; case 14: k2 |= (key[roundedEnd + 13] & 0xffL) << 40; case 13: k2 |= (key[roundedEnd + 12] & 0xffL) << 32; case 12: k2 |= (key[roundedEnd + 11] & 0xffL) << 24; case 11: k2 |= (key[roundedEnd + 10] & 0xffL) << 16; case 10: k2 |= (key[roundedEnd + 9] & 0xffL) << 8; case 9: k2 |= (key[roundedEnd + 8] & 0xffL); k2 *= c2; k2 = Long.rotateLeft(k2, 33); k2 *= c1; h2 ^= k2; case 8: k1 = ((long) key[roundedEnd + 7]) << 56; case 7: k1 |= (key[roundedEnd + 6] & 0xffL) << 48; case 6: k1 |= (key[roundedEnd + 5] & 0xffL) << 40; case 5: k1 |= (key[roundedEnd + 4] & 0xffL) << 32; case 4: k1 |= (key[roundedEnd + 3] & 0xffL) << 24; case 3: k1 |= (key[roundedEnd + 2] & 0xffL) << 16; case 2: k1 |= (key[roundedEnd + 1] & 0xffL) << 8; case 1: k1 |= (key[roundedEnd] & 0xffL); k1 *= c1; k1 = Long.rotateLeft(k1, 31); k1 *= c2; h1 ^= k1; } //---------- // finalization h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; return new HashValue(h1, h2); } }