/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2015 Danny Robson <danny@nerdcruft.net>
 */

#include "common.hpp"

#include "../../debug.hpp"


///////////////////////////////////////////////////////////////////////////////
namespace cruft::hash::murmur {
    template <>
    uint32_t
    tail<uint32_t> (const uint8_t *restrict bytes, size_t len)
    {
        uint32_t h = 0;

        switch (len % sizeof (uint32_t)) {
            case 3: h += bytes[2] << 16;
            case 2: h += bytes[1] <<  8;
            case 1: h += bytes[0];
                    break;

            default:
                unreachable ();
        }

        return h;
    }
}


//-----------------------------------------------------------------------------
namespace cruft::hash::murmur {
    template <>
    uint64_t
    tail<uint64_t> (const uint8_t *restrict bytes, size_t len)
    {
        uint64_t h = 0;

        switch (len % sizeof (uint64_t)) {
            case 7: h += uint64_t(bytes[6]) << 48;
            case 6: h += uint64_t(bytes[5]) << 40;
            case 5: h += uint64_t(bytes[4]) << 32;
            case 4: h += uint64_t(bytes[3]) << 24;
            case 3: h += uint64_t(bytes[2]) << 16;
            case 2: h += uint64_t(bytes[1]) <<  8;
            case 1: h += uint64_t(bytes[0]);
                    break;

            default:
                unreachable ();
        }

        return h;
    }
}


//-----------------------------------------------------------------------------
namespace cruft::hash::murmur {
    template <>
    std::array<uint32_t,4>
    tail_array<uint32_t> (const uint8_t *restrict bytes, size_t len)
    {
        std::array<uint32_t,4> result {0,0,0,0};

        switch (len % 16) {
            case 15: result[3] |= bytes[14] << 16;
            case 14: result[3] |= bytes[13] <<  8;
            case 13: result[3] |= bytes[12] <<  0;
            case 12: result[2] |= bytes[11] << 24;
            case 11: result[2] |= bytes[10] << 16;
            case 10: result[2] |= bytes[ 9] <<  8;
            case  9: result[2] |= bytes[ 8] <<  0;
            case  8: result[1] |= bytes[ 7] << 24;
            case  7: result[1] |= bytes[ 6] << 16;
            case  6: result[1] |= bytes[ 5] <<  8;
            case  5: result[1] |= bytes[ 4] <<  0;
            case  4: result[0] |= bytes[ 3] << 24;
            case  3: result[0] |= bytes[ 2] << 16;
            case  2: result[0] |= bytes[ 1] <<  8;
            case  1: result[0] |= bytes[ 0] <<  0;
                     break;

            default:
                unreachable ();
        }

        return result;
    }
}


//-----------------------------------------------------------------------------
namespace cruft::hash::murmur {
    template <>
    std::array<uint64_t,2>
    tail_array<uint64_t> (const uint8_t *restrict bytes, size_t len)
    {
        std::array<uint64_t,2> result {0, 0};

        switch(len & 15)
        {
            case 15: result[1] |= uint64_t{bytes[14]} << 48;
            case 14: result[1] |= uint64_t{bytes[13]} << 40;
            case 13: result[1] |= uint64_t{bytes[12]} << 32;
            case 12: result[1] |= uint64_t{bytes[11]} << 24;
            case 11: result[1] |= uint64_t{bytes[10]} << 16;
            case 10: result[1] |= uint64_t{bytes[ 9]} <<  8;
            case  9: result[1] |= uint64_t{bytes[ 8]} <<  0;

            case  8: result[0] |= uint64_t{bytes[ 7]} << 56;
            case  7: result[0] |= uint64_t{bytes[ 6]} << 48;
            case  6: result[0] |= uint64_t{bytes[ 5]} << 40;
            case  5: result[0] |= uint64_t{bytes[ 4]} << 32;
            case  4: result[0] |= uint64_t{bytes[ 3]} << 24;
            case  3: result[0] |= uint64_t{bytes[ 2]} << 16;
            case  2: result[0] |= uint64_t{bytes[ 1]} <<  8;
            case  1: result[0] |= uint64_t{bytes[ 0]} <<  0;
                     break;

            default:
                unreachable ();
        };

        return result;
    }
}