gov.sandia.cognition.hash.Eva32Hash Maven / Gradle / Ivy
/*
* File: Eva32Hash.java
* Authors: Kevin R. Dixon
* Company: Sandia National Laboratories
* Project: Cognitive Foundry
*
* Copyright Jan 26, 2011, Sandia Corporation.
* Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
* license for use of this work by or on behalf of the U.S. Government.
* Export of this program may require a license from the United States
* Government. See CopyrightHistory.txt for complete details.
*
*/
package gov.sandia.cognition.hash;
import gov.sandia.cognition.annotation.PublicationReference;
import gov.sandia.cognition.annotation.PublicationType;
import gov.sandia.cognition.util.ObjectUtil;
/**
* This implements the 32-bit "evahash" due to Robert Jenkins. This is a Java
* port of Jenkins's C implementation.
* @author Kevin R. Dixon
* @since 3.4.2
*/
@PublicationReference(
author="Robert J. Jenkins, Jr.",
title="Hash Functions for Hash Table Lookup",
type=PublicationType.WebPage,
year=1997,
url="http://burtleburtle.net/bob/hash/evahash.html",
notes="Ported from Robert Jenkins's C implementation"
)
public class Eva32Hash
extends AbstractHashFunction
{
/**
* A 32-bit, 4-byte hash, {@value}.
*/
public static final int LENGTH = 4;
/**
* Default seed: ( 0, 0, 0, 0 ).
*/
protected static final byte[] DEFAULT_SEED = HashFunctionUtil.toByteArray(0);
/**
* Creates a new instance of Eva32Hash
*/
public Eva32Hash()
{
}
@Override
public Eva32Hash clone()
{
Eva32Hash clone = (Eva32Hash) super.clone();
return clone;
}
@Override
public void evaluateInto(
byte[] input,
byte[] output,
byte[] seed)
{
int hash = hash( input, HashFunctionUtil.toInteger(seed) );
HashFunctionUtil.toByteArray(hash,output);
}
@Override
public int length()
{
return LENGTH;
}
/**
* 32-bit (4-byte) hash code for the given input and seed
* @param input
* Input to hash
* @param seed
* Seed, or offset, of the hash code
* @return
* 32-bit hash code of the input and seed.
*/
@SuppressWarnings("fallthrough")
public static int hash(
byte[] input,
int seed )
{
// null hashes to zero
if( input == null )
{
return 0;
}
final int inputLength = input.length;
// Initialize the values. A and b are initialized to the golden ratio,
// which is an arbitrary value.
int a = 0x9e3779b9;
int b = a;
// Since this is a 32-bit hash, just mask the low 32-bits from the seed
int c = seed;
// Initialize the bytes to the input;
byte[] k = input;
// The offset into the input array
int o = 0;
// Process 12 characters at a time until we hit the end of the string.
int remaining = inputLength;
while (remaining >= 12)
{
a += k[o+0] + (k[o+1] << 8) + (k[o+2] << 16) + (k[o+3] << 24);
b += k[o+4] + (k[o+5] << 8) + (k[o+6] << 16) + (k[o+7] << 24);
c += k[o+8] + (k[o+9] << 8) + (k[o+10] << 16) + (k[o+11] << 24);
// Jenkins uses a macro to "mix"... Java doesn't have those, so
// I'm just going to write it out: mix( a, b, c )
// There is one subtle difference between C and Java when it
// comes to unsigned arithmetic (sigh)... the right shift operator
// http://www.javamex.com/java_equivalents/unsigned.shtml
a-=b; a-=c; a^=(c >>>13);
b-=c; b-=a; b^=(a << 8);
c-=a; c-=b; c^=(b >>>13);
a-=b; a-=c; a^=(c >>>12);
b-=c; b-=a; b^=(a << 16);
c-=a; c-=b; c^=(b >>>5);
a-=b; a-=c; a^=(c >>>3);
b-=c; b-=a; b^=(a << 10);
c-=a; c-=b; c^=(b >>>15);
// We've knocked off twelve bytes, let's see how much is remaining
remaining -= 12;
o += 12;
}
// The fall-through on the switch statement is deliberate here...
// there should be no breaks in this switch statement.
c += inputLength;
switch (remaining)
{
case 11: c += k[o+10] << 24;
case 10: c += k[o+9] << 16;
case 9: c += k[o+8] << 8;
// Note: The first byte of c is reserved for the length.
case 8: b += k[o+7] << 24;
case 7: b += k[o+6] << 16;
case 6: b += k[o+5] << 8;
case 5: b += k[o+4];
case 4: a += k[o+3] << 24;
case 3: a += k[o+2] << 16;
case 2: a += k[o+1] << 8;
case 1: a += k[o+0];
}
// We have to write out mix(a,b,c) again... sigh.
a-=b; a-=c; a^=(c >>>13);
b-=c; b-=a; b^=(a << 8);
c-=a; c-=b; c^=(b >>>13);
a-=b; a-=c; a^=(c >>>12);
b-=c; b-=a; b^=(a << 16);
c-=a; c-=b; c^=(b >>>5);
a-=b; a-=c; a^=(c >>>3);
b-=c; b-=a; b^=(a << 10);
c-=a; c-=b; c^=(b >>>15);
return c;
}
@Override
public byte[] getDefaultSeed()
{
return ObjectUtil.deepCopy(DEFAULT_SEED);
}
}