#include <MurmurHash3/MurmurHash3.hpp>
#include <cstring>

uint32_t MurmurHash3::mixConstants(uint32_t k) const
{
    k *= c1;
    k = (k << r1) | (k >> (32 - r1));
    k *= c2;
    return k;
}

void MurmurHash3::processTail(const uint8_t *tail, size_t remaining)
{
    uint32_t k1 = 0;
    switch (remaining)
    {
    case 3: k1 ^= tail[2] << 16;
    case 2: k1 ^= tail[1] << 8;
    case 1: k1 ^= tail[0];
            k1 = mixConstants(k1);
            hash ^= k1;
    }
}

uint32_t MurmurHash3::finalizeMix(uint32_t h) const
{
    h ^= h >> 16;
    h *= 0x85ebca6b;
    h ^= h >> 13;
    h *= 0xc2b2ae35;
    h ^= h >> 16;
    return h;
}

void MurmurHash3::feed(const char *data, size_t len)
{
    const int nblocks = len / 4;
    
    // Process complete blocks.
    for (int i = 0; i < nblocks; i++)
    {
        uint32_t k;
        memcpy(&k, data + i * 4, sizeof(uint32_t));

        k = mixConstants(k);
        hash ^= k;
        hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
    }

    // Process the tail of the data.
    const uint8_t *tail = (const uint8_t*)(data + nblocks * 4);
    processTail(tail, len & 3);
    length += len;
}

uint32_t MurmurHash3::digest() const
{
    uint32_t finalHash = hash;
    finalHash ^= length;
    finalHash = finalizeMix(finalHash);
    return finalHash;
}