blob: ae67635b97b9d03e3c57e160f8ee8e734745ae6c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ignite.internal.processors.query.stat;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Implement Murmur8_128 hash function for byte arrays.
*/
public class Hasher {
/** */
private static final int SEED = 123456;
/** */
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 len;
/**
* Calculate hash by specified byte array.
*
* @param arr Array to calculate hash by.
* @return Hash value.
*/
public long fastHash(byte[] arr) {
return fastHash(arr, 0, arr.length);
}
/**
* Calculate hash by specified part of byte array.
*
* @param arr Array to calculate hash by.
* @param off Offset.
* @param len Length.
* @return Hash value.
*/
public long fastHash(byte[] arr, int off, int len) {
h1 = SEED;
h2 = SEED;
this.len = 0;
ByteBuffer bb = ByteBuffer.wrap(arr, off, len).order(ByteOrder.LITTLE_ENDIAN);
while (bb.remaining() >= CHUNK_SIZE)
process(bb);
if (bb.remaining() > 0)
processRemaining(bb);
return makeHash();
}
/**
* Mix two long numbers.
*
* @param k1 The first number.
* @param k2 The second number.
*/
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;
}
/**
* Process byte buffer to calculate hash by.
*
* @param bb Buffer to process.
*/
private void process(ByteBuffer bb) {
long k1 = bb.getLong();
long k2 = bb.getLong();
bmix64(k1, k2);
len += CHUNK_SIZE;
}
/**
* Get int.
*
* @param val Byte value
* @return Integer value
*/
private static int toInt(byte val) {
return val & 0xFF;
}
/**
* Process remaining bytes from byte buffer.
*
* @param bb Byte buffer to finish processing.
*/
private void processRemaining(ByteBuffer bb) {
long k1 = 0;
long k2 = 0;
len += 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);
}
/**
* Make hash from internal state.
*
* @return Long hash value.
*/
private long makeHash() {
h1 ^= len;
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
return h1;
}
/**
* Mix long value to prepare internal state for hash generation.
*
* @param k Long value.
* @return Hashed one.
*/
private static long fmix64(long k) {
k ^= k >>> 33;
k *= 0xff51afd7ed558ccdL;
k ^= k >>> 33;
k *= 0xc4ceb9fe1a85ec53L;
k ^= k >>> 33;
return k;
}
/**
* Mix value to update internal state h1.
*
* @param k1 Value to mix.
* @return New h1 value.
*/
private static long mixK1(long k1) {
k1 *= C1;
k1 = Long.rotateLeft(k1, 31);
k1 *= C2;
return k1;
}
/**
* Mix value to update internal state h2.
*
* @param k2 Value to mix.
* @return New h2 value.
*/
private static long mixK2(long k2) {
k2 *= C2;
k2 = Long.rotateLeft(k2, 33);
k2 *= C1;
return k2;
}
}