
net.openhft.chronicle.hash.hashing.Hasher Maven / Gradle / Ivy
/*
* Copyright 2014 Higher Frequency Trading
*
* http://www.higherfrequencytrading.com
*
* 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 net.openhft.chronicle.hash.hashing;
import net.openhft.lang.io.Bytes;
import static net.openhft.chronicle.hash.hashing.UnsafeAccess.BYTE_BASE;
import static net.openhft.chronicle.hash.hashing.UnsafeAccess.CHAR_BASE;
public final class Hasher {
// MurmurHash3, derived from
// https://github.com/google/guava/blob/fa95e381e665d8ee9639543b99ed38020c8de5ef
// /guava/src/com/google/common/hash/Murmur3_128HashFunction.java
private static final long C1 = 0x87c37b91114253d5L;
private static final long C2 = 0x4cf5ad432745937fL;
/**
* Returns the hash code for {@code len} continuous bytes of the given {@code input} object,
* starting from the given offset. The abstraction of input as ordered byte sequence and
* "offset within the input" is defined by the given {@code access} strategy.
*
* This method doesn't promise to throw a {@code RuntimeException} if {@code
* [off, off + len - 1]} subsequence exceeds the bounds of the bytes sequence, defined by {@code
* access} strategy for the given {@code input}, so use this method with caution.
*
* @param input the object to read bytes from
* @param access access which defines the abstraction of the given input
* as ordered byte sequence
* @param offset offset to the first byte of the subsequence to hash
* @param length length of the subsequence to hash
* @param the type of the input
* @return hash code for the specified bytes subsequence
*/
public static long hash(T input, Access access, long offset, long length) {
assert offset >= 0L && length >= 0L;
long h1 = 0L;
long h2 = 0L;
long remaining = length;
while (remaining >= 16L) {
long k1 = access.getLong(input, offset);
long k2 = access.getLong(input, offset);
offset += 16L;
remaining -= 16L;
h1 ^= mixK1(k1);
h1 = Long.rotateLeft(h1, 27);
h1 += h2;
h1 = h1 * 5L + 0x52dce729L;
h2 ^= mixK2(k2);
h2 = Long.rotateLeft(h2, 31);
h2 += h1;
h2 = h2 * 5L + 0x38495ab5L;
}
if (remaining > 0L) {
long k1 = 0L;
long k2 = 0L;
switch ((int) remaining) {
case 15:
k2 ^= ((long) access.getUnsignedByte(input, offset + 14L)) << 48;// fall through
case 14:
k2 ^= ((long) access.getUnsignedByte(input, offset + 13L)) << 40;// fall through
case 13:
k2 ^= ((long) access.getUnsignedByte(input, offset + 12L)) << 32;// fall through
case 12:
k2 ^= ((long) access.getUnsignedByte(input, offset + 11L)) << 24;// fall through
case 11:
k2 ^= ((long) access.getUnsignedByte(input, offset + 10L)) << 16;// fall through
case 10:
k2 ^= ((long) access.getUnsignedByte(input, offset + 9L)) << 8; // fall through
case 9:
k2 ^= ((long) access.getUnsignedByte(input, offset + 8L)); // fall through
case 8:
k1 ^= access.getLong(input, offset);
break;
case 7:
k1 ^= ((long) access.getUnsignedByte(input, offset + 6L)) << 48; // fall through
case 6:
k1 ^= ((long) access.getUnsignedByte(input, offset + 5L)) << 40; // fall through
case 5:
k1 ^= ((long) access.getUnsignedByte(input, offset + 4L)) << 32; // fall through
case 4:
k1 ^= access.getUnsignedInt(input, offset);
break;
case 3:
k1 ^= ((long) access.getUnsignedByte(input, offset + 2L)) << 16; // fall through
case 2:
k1 ^= ((long) access.getUnsignedByte(input, offset + 1L)) << 8; // fall through
case 1:
k1 ^= ((long) access.getUnsignedByte(input, offset));
case 0:
break;
default:
throw new AssertionError("Should never get here.");
}
h1 ^= mixK1(k1);
h2 ^= mixK2(k2);
}
return finalize(length, h1, h2);
}
private static long finalize(long length, long h1, long h2) {
h1 ^= length;
h2 ^= length;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
return h1;
}
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;
}
public static long hash(Bytes bytes) {
return hash(bytes, bytes.position(), bytes.limit());
}
public static long hash(Bytes bytes, long offset, long limit) {
return hash(bytes, Accesses.toBytes(), offset, limit - offset);
}
public static long hash(byte[] array) {
return hash(array, Accesses.unsafe(), BYTE_BASE, (long) array.length);
}
public static long hash(char[] array) {
return hash(array, Accesses.unsafe(), CHAR_BASE, array.length * 2L);
}
public static long hash(int value) {
long k1 = Primitives.unsignedInt(value);
long h1 = 0L ^ mixK1(k1);
long h2 = 0L;
return finalize(4L, h1, h2);
}
public static long hash(long value) {
long k1 = value;
long h1 = 0L ^ mixK1(k1);
long h2 = 0L;
return finalize(8L, h1, h2);
}
private Hasher() {}
}