package org.jruby.util;

public class MurmurHash {
    // Based on Murmurhash 2.0 Java port at http://dmy999.com/article/50/murmurhash-2-java-port
    // 2011-12-05: Modified by Hiroshi Nakamura <nahi@ruby-lang.org>
    // - signature change to use offset
    //   hash(byte[] data, int seed) to hash(byte[] src, int offset, int length, int seed)
    // - extract 'm' and 'r' as murmurhash2.0 constants

    // Ported by Derek Young from the C version (specifically the endian-neutral
    // version) from:
    //   http://murmurhash.googlepages.com/
    //
    // released to the public domain - dmy999@gmail.com

    // 'm' and 'r' are mixing constants generated offline.
    // They're not really 'magic', they just happen to work well.
    public static final int MURMUR2_MAGIC = 0x5bd1e995;
    // CRuby 1.9 uses 16 but original C++ implementation uses 24 with above Magic.
    private static final int MURMUR2_R = 24;

    @SuppressWarnings("fallthrough")
    public static int hash32(byte[] src, int offset, int length, int seed) {
        // Initialize the hash to a 'random' value
        int h = seed ^ length;

        int i = offset;
        int len = length;
        while (len >= 4) {
            int k = src[i + 0] & 0xFF;
            k |= (src[i + 1] & 0xFF) << 8;
            k |= (src[i + 2] & 0xFF) << 16;
            k |= (src[i + 3] & 0xFF) << 24;

            k *= MURMUR2_MAGIC;
            k ^= k >>> MURMUR2_R;
            k *= MURMUR2_MAGIC;

            h *= MURMUR2_MAGIC;
            h ^= k;

            i += 4;
            len -= 4;
        }

        switch (len) {
        case 3:
            h ^= (src[i + 2] & 0xFF) << 16;
        case 2:
            h ^= (src[i + 1] & 0xFF) << 8;
        case 1:
            h ^= (src[i + 0] & 0xFF);
            h *= MURMUR2_MAGIC;
        }

        h ^= h >>> 13;
        h *= MURMUR2_MAGIC;
        h ^= h >>> 15;

        return h;
    }
}