
com.hazelcast.util.HashUtil Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
* Portions Copyright 2014 the original author or 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.hazelcast.util;
import com.hazelcast.nio.UnsafeHelper;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import sun.misc.Unsafe;
import java.nio.ByteOrder;
import java.util.Arrays;
import static com.hazelcast.util.Preconditions.checkPositive;
import static java.lang.Math.abs;
@SuppressFBWarnings({"SF_SWITCH_FALLTHROUGH", "SF_SWITCH_NO_DEFAULT"})
public final class HashUtil {
private static final boolean LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN == ByteOrder.nativeOrder();
private static final int DEFAULT_MURMUR_SEED = 0x01000193;
public static int MurmurHash3_x86_32(byte[] data, int offset, int len) {
return MurmurHash3_x86_32(data, offset, len, DEFAULT_MURMUR_SEED);
}
/**
* Returns the MurmurHash3_x86_32 hash.
*/
public static int MurmurHash3_x86_32(byte[] data, int offset, int len, int seed) {
int c1 = 0xcc9e2d51;
int c2 = 0x1b873593;
int h1 = seed;
int roundedEnd = offset + (len & 0xfffffffc); // round down to 4 byte block
for (int i = offset; i < roundedEnd; i += 4) {
// little endian load order
int k1 = (data[i] & 0xff)
| ((data[i + 1] & 0xff) << 8)
| ((data[i + 2] & 0xff) << 16)
| (data[i + 3] << 24);
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13);
h1 = h1 * 5 + 0xe6546b64;
}
// tail
int k1 = 0;
switch (len & 0x03) {
case 3:
k1 = (data[roundedEnd + 2] & 0xff) << 16;
// fallthrough
case 2:
k1 |= (data[roundedEnd + 1] & 0xff) << 8;
// fallthrough
case 1:
k1 |= data[roundedEnd] & 0xff;
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
}
// finalization
h1 ^= len;
h1 = MurmurHash3_fmix(h1);
return h1;
}
public static int MurmurHash3_x86_32_direct(long address, int offset, int len) {
return MurmurHash3_x86_32_direct(address, offset, len, DEFAULT_MURMUR_SEED);
}
/**
* Returns the MurmurHash3_x86_32 hash.
*/
public static int MurmurHash3_x86_32_direct(long address, int offset, int len, int seed) {
int c1 = 0xcc9e2d51;
int c2 = 0x1b873593;
int h1 = seed;
int roundedEnd = offset + (len & 0xfffffffc); // round down to 4 byte block
Unsafe unsafe = UnsafeHelper.UNSAFE;
for (int i = offset; i < roundedEnd; i += 4) {
// little endian load order
int k1 = LITTLE_ENDIAN ? unsafe.getInt(address + i)
: (unsafe.getByte(address + i) & 0xff)
| ((unsafe.getByte(address + i + 1) & 0xff) << 8)
| ((unsafe.getByte(address + i + 2) & 0xff) << 16)
| (unsafe.getByte(address + i + 3) << 24);
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13);
h1 = h1 * 5 + 0xe6546b64;
}
// tail
int k1 = 0;
switch (len & 0x03) {
case 3:
k1 = (unsafe.getByte(address + roundedEnd + 2) & 0xff) << 16;
// fallthrough
case 2:
k1 |= (unsafe.getByte(address + roundedEnd + 1) & 0xff) << 8;
// fallthrough
case 1:
k1 |= unsafe.getByte(address + roundedEnd) & 0xff;
k1 *= c1;
k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
}
// finalization
h1 ^= len;
h1 = MurmurHash3_fmix(h1);
return h1;
}
public static long MurmurHash3_x64_64(final byte[] data, int offset, int len) {
return MurmurHash3_x64_64(data, offset, len, DEFAULT_MURMUR_SEED);
}
public static long MurmurHash3_x64_64(final byte[] data, int offset, int len, final int seed) {
long h1 = 0x9368e53c2f6af274L ^ seed;
long h2 = 0x586dcd208f7cd3fdL ^ seed;
long c1 = 0x87c37b91114253d5L;
long c2 = 0x4cf5ad432745937fL;
long k1 = 0;
long k2 = 0;
for (int i = 0; i < len / 16; i++) {
k1 = MurmurHash3_getBlock(data, (i * 2 * 8) + offset);
k2 = MurmurHash3_getBlock(data, ((i * 2 + 1) * 8) + offset);
// bmix(state);
k1 *= c1;
k1 = (k1 << 23) | (k1 >>> 64 - 23);
k1 *= c2;
h1 ^= k1;
h1 += h2;
h2 = (h2 << 41) | (h2 >>> 64 - 41);
k2 *= c2;
k2 = (k2 << 23) | (k2 >>> 64 - 23);
k2 *= c1;
h2 ^= k2;
h2 += h1;
h1 = h1 * 3 + 0x52dce729;
h2 = h2 * 3 + 0x38495ab5;
c1 = c1 * 5 + 0x7b7d159c;
c2 = c2 * 5 + 0x6bce6396;
}
k1 = 0;
k2 = 0;
int tail = ((len >>> 4) << 4) + offset;
switch (len & 15) {
case 15:
k2 ^= (long) data[tail + 14] << 48;
case 14:
k2 ^= (long) data[tail + 13] << 40;
case 13:
k2 ^= (long) data[tail + 12] << 32;
case 12:
k2 ^= (long) data[tail + 11] << 24;
case 11:
k2 ^= (long) data[tail + 10] << 16;
case 10:
k2 ^= (long) data[tail + 9] << 8;
case 9:
k2 ^= data[tail + 8];
case 8:
k1 ^= (long) data[tail + 7] << 56;
case 7:
k1 ^= (long) data[tail + 6] << 48;
case 6:
k1 ^= (long) data[tail + 5] << 40;
case 5:
k1 ^= (long) data[tail + 4] << 32;
case 4:
k1 ^= (long) data[tail + 3] << 24;
case 3:
k1 ^= (long) data[tail + 2] << 16;
case 2:
k1 ^= (long) data[tail + 1] << 8;
case 1:
k1 ^= data[tail];
// bmix();
k1 *= c1;
k1 = (k1 << 23) | (k1 >>> 64 - 23);
k1 *= c2;
h1 ^= k1;
h1 += h2;
h2 = (h2 << 41) | (h2 >>> 64 - 41);
k2 *= c2;
k2 = (k2 << 23) | (k2 >>> 64 - 23);
k2 *= c1;
h2 ^= k2;
h2 += h1;
h1 = h1 * 3 + 0x52dce729;
h2 = h2 * 3 + 0x38495ab5;
// c1 = c1 * 5 + 0x7b7d159c; // unused, used only for 128-bit version
// c2 = c2 * 5 + 0x6bce6396; // unused, used only for 128-bit version
}
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = MurmurHash3_fmix(h1);
h2 = MurmurHash3_fmix(h2);
h1 += h2;
// h2 += h1; // unused, used only for 128-bit version
return h1;
}
public static long MurmurHash3_x64_64_direct(long address, int offset, int len) {
return MurmurHash3_x64_64_direct(address, offset, len, DEFAULT_MURMUR_SEED);
}
public static long MurmurHash3_x64_64_direct(long address, int offset, int len, final int seed) {
long h1 = 0x9368e53c2f6af274L ^ seed;
long h2 = 0x586dcd208f7cd3fdL ^ seed;
long c1 = 0x87c37b91114253d5L;
long c2 = 0x4cf5ad432745937fL;
long k1 = 0;
long k2 = 0;
for (int i = 0; i < len / 16; i++) {
k1 = MurmurHash3_getBlock_direct(address, (i * 2 * 8) + offset);
k2 = MurmurHash3_getBlock_direct(address, ((i * 2 + 1) * 8) + offset);
// bmix(state);
k1 *= c1;
k1 = (k1 << 23) | (k1 >>> 64 - 23);
k1 *= c2;
h1 ^= k1;
h1 += h2;
h2 = (h2 << 41) | (h2 >>> 64 - 41);
k2 *= c2;
k2 = (k2 << 23) | (k2 >>> 64 - 23);
k2 *= c1;
h2 ^= k2;
h2 += h1;
h1 = h1 * 3 + 0x52dce729;
h2 = h2 * 3 + 0x38495ab5;
c1 = c1 * 5 + 0x7b7d159c;
c2 = c2 * 5 + 0x6bce6396;
}
k1 = 0;
k2 = 0;
int tail = ((len >>> 4) << 4) + offset;
Unsafe unsafe = UnsafeHelper.UNSAFE;
switch (len & 15) {
case 15:
k2 ^= (long) unsafe.getByte(address + tail + 14) << 48;
case 14:
k2 ^= (long) unsafe.getByte(address + tail + 13) << 40;
case 13:
k2 ^= (long) unsafe.getByte(address + tail + 12) << 32;
case 12:
k2 ^= (long) unsafe.getByte(address + tail + 11) << 24;
case 11:
k2 ^= (long) unsafe.getByte(address + tail + 10) << 16;
case 10:
k2 ^= (long) unsafe.getByte(address + tail + 9) << 8;
case 9:
k2 ^= unsafe.getByte(address + tail + 8);
case 8:
k1 ^= (long) unsafe.getByte(address + tail + 7) << 56;
case 7:
k1 ^= (long) unsafe.getByte(address + tail + 6) << 48;
case 6:
k1 ^= (long) unsafe.getByte(address + tail + 5) << 40;
case 5:
k1 ^= (long) unsafe.getByte(address + tail + 4) << 32;
case 4:
k1 ^= (long) unsafe.getByte(address + tail + 3) << 24;
case 3:
k1 ^= (long) unsafe.getByte(address + tail + 2) << 16;
case 2:
k1 ^= (long) unsafe.getByte(address + tail + 1) << 8;
case 1:
k1 ^= unsafe.getByte(address + tail);
// bmix();
k1 *= c1;
k1 = (k1 << 23) | (k1 >>> 64 - 23);
k1 *= c2;
h1 ^= k1;
h1 += h2;
h2 = (h2 << 41) | (h2 >>> 64 - 41);
k2 *= c2;
k2 = (k2 << 23) | (k2 >>> 64 - 23);
k2 *= c1;
h2 ^= k2;
h2 += h1;
h1 = h1 * 3 + 0x52dce729;
h2 = h2 * 3 + 0x38495ab5;
// c1 = c1 * 5 + 0x7b7d159c; // unused, used only for 128-bit version
// c2 = c2 * 5 + 0x6bce6396; // unused, used only for 128-bit version
}
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = MurmurHash3_fmix(h1);
h2 = MurmurHash3_fmix(h2);
h1 += h2;
// h2 += h1; // unused, used only for 128-bit version
return h1;
}
private static long MurmurHash3_getBlock(byte[] key, int i) {
return ((key[i] & 0x00000000000000FFL))
| ((key[i + 1] & 0x00000000000000FFL) << 8)
| ((key[i + 2] & 0x00000000000000FFL) << 16)
| ((key[i + 3] & 0x00000000000000FFL) << 24)
| ((key[i + 4] & 0x00000000000000FFL) << 32)
| ((key[i + 5] & 0x00000000000000FFL) << 40)
| ((key[i + 6] & 0x00000000000000FFL) << 48)
| ((key[i + 7] & 0x00000000000000FFL) << 56);
}
private static long MurmurHash3_getBlock_direct(long address, int i) {
Unsafe unsafe = UnsafeHelper.UNSAFE;
if (LITTLE_ENDIAN) {
return unsafe.getLong(address + i);
} else {
return ((unsafe.getByte(address + i) & 0x00000000000000FFL))
| ((unsafe.getByte(address + i + 1) & 0x00000000000000FFL) << 8)
| ((unsafe.getByte(address + i + 2) & 0x00000000000000FFL) << 16)
| ((unsafe.getByte(address + i + 3) & 0x00000000000000FFL) << 24)
| ((unsafe.getByte(address + i + 4) & 0x00000000000000FFL) << 32)
| ((unsafe.getByte(address + i + 5) & 0x00000000000000FFL) << 40)
| ((unsafe.getByte(address + i + 6) & 0x00000000000000FFL) << 48)
| ((unsafe.getByte(address + i + 7) & 0x00000000000000FFL) << 56);
}
}
public static int MurmurHash3_fmix(int k) {
k ^= k >>> 16;
k *= 0x85ebca6b;
k ^= k >>> 13;
k *= 0xc2b2ae35;
k ^= k >>> 16;
return k;
}
public static long MurmurHash3_fmix(long k) {
k ^= k >>> 33;
k *= 0xff51afd7ed558ccdL;
k ^= k >>> 33;
k *= 0xc4ceb9fe1a85ec53L;
k ^= k >>> 33;
return k;
}
public static long fastLongMix(long k) {
// phi = 2^64 / goldenRatio
final long phi = 0x9E3779B97F4A7C15L;
long h = k * phi;
h ^= h >>> 32;
return h ^ (h >>> 16);
}
public static int fastIntMix(int k) {
// phi = 2^32 / goldenRatio
final int phi = 0x9E3779B9;
final int h = k * phi;
return h ^ (h >>> 16);
}
/**
* Hash code for multiple objects using {@link Arrays#hashCode(Object[])}.
*/
public static int hashCode(Object... objects) {
return Arrays.hashCode(objects);
}
/**
* A function that calculates the index (e.g. to be used in an array/list) for a given hash. The returned value will always
* be equal or larger than 0 and will always be smaller than 'length'.
*
* The reason this function exists is to deal correctly with negative and especially the Integer.MIN_VALUE; since that can't
* be used safely with a Math.abs function.
*
* @param hash
* @param length the length of the array/list
* @return the mod of the hash
* @throws IllegalArgumentException if mod smaller than 1.
*/
public static int hashToIndex(int hash, int length) {
checkPositive(length, "mod must be larger than 0");
if (hash == Integer.MIN_VALUE) {
hash = 0;
} else {
hash = abs(hash);
}
return hash % length;
}
private HashUtil() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy