All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy