Murmur3Hash.java

/*
 *      Copyright (C) 2014 Robert Stupp, Koeln, Germany, robert-stupp.de
 *
 *   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 org.caffinitas.ohc.chunked;

import java.nio.ByteBuffer;

import static org.caffinitas.ohc.util.ByteBufferCompat.byteBufferPosition;

final class Murmur3Hash extends Hasher
{
    long hash(ByteBuffer buffer)
    {
        long h1 = 0L;
        long h2 = 0L;
        long k1, k2;
        long length = buffer.remaining();

        while (buffer.remaining() >= 16)
        {
            k1 = getLong(buffer);
            k2 = getLong(buffer);

            // bmix64()

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

        int r = buffer.remaining();
        if (r > 0)
        {
            k1 = 0;
            k2 = 0;
            int p = buffer.position();
            switch (r)
            {
                case 15:
                    k2 ^= toLong(buffer.get(p + 14)) << 48; // fall through
                case 14:
                    k2 ^= toLong(buffer.get(p + 13)) << 40; // fall through
                case 13:
                    k2 ^= toLong(buffer.get(p + 12)) << 32; // fall through
                case 12:
                    k2 ^= toLong(buffer.get(p + 11)) << 24; // fall through
                case 11:
                    k2 ^= toLong(buffer.get(p + 10)) << 16; // fall through
                case 10:
                    k2 ^= toLong(buffer.get(p + 9)) << 8; // fall through
                case 9:
                    k2 ^= toLong(buffer.get(p + 8)); // fall through
                case 8:
                    k1 ^= getLong(buffer);
                    break;
                case 7:
                    k1 ^= toLong(buffer.get(p + 6)) << 48; // fall through
                case 6:
                    k1 ^= toLong(buffer.get(p + 5)) << 40; // fall through
                case 5:
                    k1 ^= toLong(buffer.get(p + 4)) << 32; // fall through
                case 4:
                    k1 ^= toLong(buffer.get(p + 3)) << 24; // fall through
                case 3:
                    k1 ^= toLong(buffer.get(p + 2)) << 16; // fall through
                case 2:
                    k1 ^= toLong(buffer.get(p + 1)) << 8; // fall through
                case 1:
                    k1 ^= toLong(buffer.get(p));
                    break;
                default:
                    throw new AssertionError("Should never get here.");
            }
            byteBufferPosition(buffer, p + r);

            h1 ^= mixK1(k1);
            h2 ^= mixK2(k2);
        }

        // makeHash()

        h1 ^= length;
        h2 ^= length;

        h1 += h2;
        h2 += h1;

        h1 = fmix64(h1);
        h2 = fmix64(h2);

        h1 += h2;
        //h2 += h1;

        // padToLong()

        return h1;
    }

    private static long getLong(ByteBuffer buffer)
    {
        int o = buffer.position();
        long l = toLong(buffer.get(o + 7)) << 56;
        l |= toLong(buffer.get(o + 6)) << 48;
        l |= toLong(buffer.get(o + 5)) << 40;
        l |= toLong(buffer.get(o + 4)) << 32;
        l |= toLong(buffer.get(o + 3)) << 24;
        l |= toLong(buffer.get(o + 2)) << 16;
        l |= toLong(buffer.get(o + 1)) << 8;
        l |= toLong(buffer.get(o));
        byteBufferPosition(buffer, o + 8);
        return l;
    }
    static final long C1 = 0x87c37b91114253d5L;
    static final long C2 = 0x4cf5ad432745937fL;

    static long fmix64(long k)
    {
        k ^= k >>> 33;
        k *= 0xff51afd7ed558ccdL;
        k ^= k >>> 33;
        k *= 0xc4ceb9fe1a85ec53L;
        k ^= k >>> 33;
        return k;
    }

    static long mixK1(long k1)
    {
        k1 *= C1;
        k1 = Long.rotateLeft(k1, 31);
        k1 *= C2;
        return k1;
    }

    static long mixK2(long k2)
    {
        k2 *= C2;
        k2 = Long.rotateLeft(k2, 33);
        k2 *= C1;
        return k2;
    }

    static long toLong(byte value)
    {
        return value & 0xff;
    }
}