summaryrefslogtreecommitdiff
path: root/deimos/core/hash.h
blob: e6e9cc87e7e86f03c917a3be0a2a6b4b846cb138 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#pragma once

#include "deimos/core/base.h"

namespace deimos
{

constexpr uint64 MurmurHash3_GetBlock64(const char* key, uint64 block)
{
    // NOLINTBEGIN
    key += block * 8;

    if consteval
    {
        return
               (uint64)((uint8)key[0])
            | ((uint64)((uint8)key[1]) << 8)
            | ((uint64)((uint8)key[2]) << 16)
            | ((uint64)((uint8)key[3]) << 24)
            | ((uint64)((uint8)key[4]) << 32)
            | ((uint64)((uint8)key[5]) << 40)
            | ((uint64)((uint8)key[6]) << 48)
            | ((uint64)((uint8)key[7]) << 56);
    }
    else
    {
        uint64 value{};
        __builtin_memcpy(&value, key, 8);
        return value;
    }
    // NOLINTEND
}

constexpr uint64 MurmurHash3_Fmix64(uint64 k)
{
    k ^= k >> 33U;
    k *= 0xff51afd7ed558ccdULL;
    k ^= k >> 33U;
    k *= 0xc4ceb9fe1a85ec53ULL;
    k ^= k >> 33U;
    return k;
}

constexpr uint128 MurmurHash3_x64_128(const char* key, uint64 len)
{
    if consteval { return { 12, 12 }; }

    // NOLINTBEGIN
    const uint64 nblocks = len / 16;

    const int64 seed = 0;
    uint64 h1 = seed;
    uint64 h2 = seed;

    const uint64 c1 = 0x87c37b91114253d5ULL;
    const uint64 c2 = 0x4cf5ad432745937fULL;

    //----------
    // body

    for(uint64 i = 0; i < nblocks; i++)
    {
        uint64 k1 = MurmurHash3_GetBlock64(key, i * 2 + 0);
        uint64 k2 = MurmurHash3_GetBlock64(key, i * 2 + 1);

        k1 *= c1; k1  = _rotl64(k1, 31); k1 *= c2; h1 ^= k1;
        h1 = _rotl64(h1, 27); h1 += h2; h1 = h1*5+0x52dce729;
        k2 *= c2; k2  = _rotl64(k2, 33); k2 *= c1; h2 ^= k2;
        h2 = _rotl64(h2, 31); h2 += h1; h2 = h2*5+0x38495ab5;
    }

    //----------
    // tail

    const char* tail = key + nblocks * 16;

    uint64 k1 = 0;
    uint64 k2 = 0;

    switch (len & 15ULL)
    {
        case 15: k2 ^= ((uint64)(uint8)tail[14]) << 48U; [[fallthrough]];
        case 14: k2 ^= ((uint64)(uint8)tail[13]) << 40U; [[fallthrough]];
        case 13: k2 ^= ((uint64)(uint8)tail[12]) << 32U; [[fallthrough]];
        case 12: k2 ^= ((uint64)(uint8)tail[11]) << 24U; [[fallthrough]];
        case 11: k2 ^= ((uint64)(uint8)tail[10]) << 16U; [[fallthrough]];
        case 10: k2 ^= ((uint64)(uint8)tail[ 9]) << 8U; [[fallthrough]];
        case  9: k2 ^= ((uint64)(uint8)tail[ 8]) << 0U;
            k2 *= c2; k2  = _rotl64(k2,33); k2 *= c1; h2 ^= k2;
            [[fallthrough]];
        case  8: k1 ^= ((uint64)(uint8)tail[ 7]) << 56U; [[fallthrough]];
        case  7: k1 ^= ((uint64)(uint8)tail[ 6]) << 48U; [[fallthrough]];
        case  6: k1 ^= ((uint64)(uint8)tail[ 5]) << 40U; [[fallthrough]];
        case  5: k1 ^= ((uint64)(uint8)tail[ 4]) << 32U; [[fallthrough]];
        case  4: k1 ^= ((uint64)(uint8)tail[ 3]) << 24U; [[fallthrough]];
        case  3: k1 ^= ((uint64)(uint8)tail[ 2]) << 16U; [[fallthrough]];
        case  2: k1 ^= ((uint64)(uint8)tail[ 1]) << 8U; [[fallthrough]];
        case  1: k1 ^= ((uint64)(uint8)tail[ 0]) << 0U;
            k1 *= c1; k1  = _rotl64(k1,31); k1 *= c2; h1 ^= k1;
    };

    //----------
    // finalization

    h1 ^= len; h2 ^= len;

    h1 += h2;
    h2 += h1;

    h1 = MurmurHash3_Fmix64(h1);
    h2 = MurmurHash3_Fmix64(h2);

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

    return uint128{h1, h2};
}

constexpr uint128 MurmurHash3_x64_128(gsl::czstring str)
{
    return MurmurHash3_x64_128(str, __builtin_strlen(str));
}

} // namespace deimos