/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* 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.linkedin.databus.core.util;
import java.nio.ByteBuffer;
/**
* Implementation of Fnv-1a hash
*/
public class Fnv1aHashImpl
{
//Constants sourced from http://www.isthe.com/chongo/tech/comp/fnv/
/** seed for 32-bit hashes */
private static final long FNV_BASIS_32 = 2166136261L;
/** prime for 32-bit hashes */
private static final long FNV_PRIME_32 = 16777619L;
/** Initializes the hash accumulator */
public static final long init32()
{
return FNV_BASIS_32;
}
/**
* Adds a byte to a hash accumulator
* @param hashAccum the currently accumulated hash value
* @param v the byte value to add to the hash
* @return the new hash accumulator value
*/
public static final long addByte32(long hashAccum, byte v)
{
hashAccum ^= 0xFF & (long)v;
hashAccum *= FNV_PRIME_32;
return hashAccum;
}
/**
* Convenience method to add a short to a hash accumulator. It adds both bytes in MSB order.
* @param hashAccum the currently accumulated hash value
* @param v the short value to add to the hash
* @return the new hash accumulator value
*/
public static final long addShort32(long hashAccum, short v)
{
return addByte32(addByte32(hashAccum, (byte)((v >> 8) & 0xFF)), (byte)(v & 0xFF));
}
/**
* Convenience method to add an int to a hash accumulator. It adds all 4 bytes in MSB order.
* @param hashAccum the currently accumulated hash value
* @param v the int value to add to the hash
* @return the new hash accumulator value
*/
public static final long addInt32(long hashAccum, int v)
{
return addShort32(addShort32(hashAccum, (short)((v >> 16) & 0xFFFF)), (short)(v & 0xFFFF));
}
/**
* Convenience method to add a long to a hash accumulator. It adds all 8 bytes in MSB order.
* @param hashAccum the currently accumulated hash value
* @param v the long value to add to the hash
* @return the new hash accumulator value
*/
public static final long addLong32(long hashAccum, long v)
{
return addInt32(addInt32(hashAccum, (int)((v >> 32) & 0xFFFFFFFF)), (int)(v & 0xFFFFFFFF));
}
/**
* Convenience method to add a (subsequence in a) byte array to a hash accumulator.
* @param hashAccum the currently accumulated hash value
* @param a the byte array
* @param startIdx the index of the first byte to hash
* @param num the number of bytes to hash
* @return the new hash accumulator value
*/
public static final long addBytes32(long hashAccum, byte[] a, int startIdx, int num)
{
final int n = startIdx + num;
for (int i = startIdx; i < n; ++i)
{
hashAccum = addByte32(hashAccum, a[i]);
}
return hashAccum;
}
/**
* Convenience method to add a byte array to a hash accumulator.
* @param hashAccum the currently accumulated hash value
* @param a the byte array
* @return the new hash accumulator value
*/
public static final long addBytes32(long hashAccum, byte[] a)
{
return addBytes32(hashAccum, a, 0, a.length);
}
/**
* Convenience method to add a (subsequence in a) ByteBuffer to a hash accumulator.
* @param hashAccum the currently accumulated hash value
* @param buf the byte buffer
* @param startIdx the index of the first byte to hash
* @param num the number of bytes to hash
* @return the new hash accumulator value
*/
public static long addByteBuffer32(long hashAccum, ByteBuffer buf, int startIdx, int num)
{
if (buf.hasArray())
{
return addBytes32(hashAccum, buf.array(), startIdx, num);
}
final int last = Math.min(startIdx + num, buf.limit());
for (int i=startIdx; i<last; i++)
{
hashAccum=addByte32(hashAccum, buf.get(i));
}
return hashAccum;
}
/**
* Convenience method to add a ByteBuffer to a hash accumulator.
* @param hashAccum the currently accumulated hash value
* @param buf the byte buffer
* @return the new hash accumulator value
*/
public static long addByteBuffer32(long hashAccum, ByteBuffer buf)
{
return addByteBuffer32(hashAccum, buf, 0, buf.limit());
}
/**
* Convenience method to add a (substring of a)string
* @param hashAccum the currently accumulated hash value
* @param s the string
* @param startIdx the index of the first byte to hash
* @param num the number of bytes to hash
* @return the new hash accumulator value
*/
public static long addString32(long hashAccum, String s, int startIdx, int num)
{
final int endIdx = startIdx + num;
for (int i = startIdx; i < endIdx; ++i)
{
//technically the Java characters are 2 bytes but we use only the lower 8 bits for performance.
hashAccum = addByte32(hashAccum, (byte)(s.codePointAt(i) & 0xFF));
}
return hashAccum;
}
/**
* Convenience method to add a string
* @param hashAccum the currently accumulated hash value
* @param s the string
* @return the new hash accumulator value
*/
public static long addString32(long hashAccum, String s)
{
return addString32(hashAccum, s, 0, s.length());
}
/**
* Obtain the 32-bit hash value accumulated so far
* @param hashAccum the hash accumulated so far
* @return the hash value as an int
*/
public static final int getHash32(long hashAccum)
{
return (int)(hashAccum & 0xFFFFFFFF);
}
/**
* Obtain the 32-bit hash value accumulated so far for a given bucket
* @param hashAccum the hash accumulated so far
* @param numBuckets the number of buckets
* @return the hash value as an int in [0, bucketNum)
*/
public static final int getHash32(long hashAccum, int numBuckets)
{
assert numBuckets > 0;
//make sure we are protected against negative hash values
return (int)((hashAccum & 0x7FFFFFFFFFFFFFFFL) % numBuckets);
}
}