1b1994897Sopenharmony_ci/**
2b1994897Sopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3b1994897Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4b1994897Sopenharmony_ci * you may not use this file except in compliance with the License.
5b1994897Sopenharmony_ci * You may obtain a copy of the License at
6b1994897Sopenharmony_ci *
7b1994897Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
8b1994897Sopenharmony_ci *
9b1994897Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10b1994897Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11b1994897Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12b1994897Sopenharmony_ci * See the License for the specific language governing permissions and
13b1994897Sopenharmony_ci * limitations under the License.
14b1994897Sopenharmony_ci */
15b1994897Sopenharmony_ci
16b1994897Sopenharmony_ci#ifndef LIBPANDABASE_UTILS_HASH_H
17b1994897Sopenharmony_ci#define LIBPANDABASE_UTILS_HASH_H
18b1994897Sopenharmony_ci
19b1994897Sopenharmony_ci#include "murmur3_hash.h"
20b1994897Sopenharmony_ci
21b1994897Sopenharmony_cinamespace panda {
22b1994897Sopenharmony_ci
23b1994897Sopenharmony_ci// Now, murmur3 hash is used by default
24b1994897Sopenharmony_citemplate <uint32_t seed_value>
25b1994897Sopenharmony_ciusing DefaultHash = MurmurHash32<seed_value>;
26b1994897Sopenharmony_ci
27b1994897Sopenharmony_ci// Default seed which is used in hash functions.
28b1994897Sopenharmony_ci// NOTE: To create different seed for your purposes,
29b1994897Sopenharmony_ci// one must define it here and create new alias hash class
30b1994897Sopenharmony_cistatic constexpr uint32_t DEFAULT_SEED = 0x12345678U;
31b1994897Sopenharmony_ci
32b1994897Sopenharmony_ci// Hash class alias with the default seed inside.
33b1994897Sopenharmony_ciusing Hash = DefaultHash<DEFAULT_SEED>;
34b1994897Sopenharmony_ci
35b1994897Sopenharmony_ci/**
36b1994897Sopenharmony_ci * \brief Create 32 bits Hash from \param key via \param seed.
37b1994897Sopenharmony_ci * @param key - a key which should be hashed
38b1994897Sopenharmony_ci * @param len - length of the key in bytes
39b1994897Sopenharmony_ci * @param seed - seed which is used to calculate hash
40b1994897Sopenharmony_ci * @return 32 bits hash
41b1994897Sopenharmony_ci */
42b1994897Sopenharmony_ciinline uint32_t GetHash32WithSeed(const uint8_t *key, size_t len, uint32_t seed)
43b1994897Sopenharmony_ci{
44b1994897Sopenharmony_ci    return Hash::GetHash32WithSeed(key, len, seed);
45b1994897Sopenharmony_ci}
46b1994897Sopenharmony_ci
47b1994897Sopenharmony_ci/**
48b1994897Sopenharmony_ci * \brief Create 32 bits Hash from \param key.
49b1994897Sopenharmony_ci * @param key - a key which should be hashed
50b1994897Sopenharmony_ci * @param len - length of the key in bytes
51b1994897Sopenharmony_ci * @return 32 bits hash
52b1994897Sopenharmony_ci */
53b1994897Sopenharmony_ciinline uint32_t GetHash32(const uint8_t *key, size_t len)
54b1994897Sopenharmony_ci{
55b1994897Sopenharmony_ci    return Hash::GetHash32(key, len);
56b1994897Sopenharmony_ci}
57b1994897Sopenharmony_ci
58b1994897Sopenharmony_ci/**
59b1994897Sopenharmony_ci * \brief Create 32 bits Hash from MUTF8 \param string.
60b1994897Sopenharmony_ci * @param string - a pointer to the MUTF8 string
61b1994897Sopenharmony_ci * @return 32 bits hash
62b1994897Sopenharmony_ci */
63b1994897Sopenharmony_ciinline uint32_t GetHash32String(const uint8_t *mutf8_string)
64b1994897Sopenharmony_ci{
65b1994897Sopenharmony_ci    return Hash::GetHash32String(mutf8_string);
66b1994897Sopenharmony_ci}
67b1994897Sopenharmony_ci
68b1994897Sopenharmony_ci/**
69b1994897Sopenharmony_ci * \brief Create 32 bits Hash from MUTF8 \param string.
70b1994897Sopenharmony_ci * @param string - a pointer to the MUTF8 string
71b1994897Sopenharmony_ci * @param seed - seed which is used to calculate hash
72b1994897Sopenharmony_ci * @return 32 bits hash
73b1994897Sopenharmony_ci */
74b1994897Sopenharmony_ciinline uint32_t GetHash32StringWithSeed(const uint8_t *mutf8_string, uint32_t seed)
75b1994897Sopenharmony_ci{
76b1994897Sopenharmony_ci    return Hash::GetHash32StringWithSeed(mutf8_string, seed);
77b1994897Sopenharmony_ci}
78b1994897Sopenharmony_ci
79b1994897Sopenharmony_ciconstexpr uint32_t FNV_INITIAL_SEED = 0x811c9dc5;
80b1994897Sopenharmony_ci
81b1994897Sopenharmony_ci/// Works like FNV hash but operates over 4-byte words at a time instead of single bytes
82b1994897Sopenharmony_citemplate <typename Item>
83b1994897Sopenharmony_ciuint32_t PseudoFnvHashItem(Item item, uint32_t seed = FNV_INITIAL_SEED)
84b1994897Sopenharmony_ci{
85b1994897Sopenharmony_ci    // NOLINTNEXTLINE(readability-braces-around-statements)
86b1994897Sopenharmony_ci    if constexpr (sizeof(Item) <= 4) {
87b1994897Sopenharmony_ci        constexpr uint32_t PRIME = 16777619U;
88b1994897Sopenharmony_ci        return (seed ^ static_cast<uint32_t>(item)) * PRIME;
89b1994897Sopenharmony_ci    } else if constexpr (sizeof(Item) == 8) {  // NOLINT(readability-misleading-indentation)
90b1994897Sopenharmony_ci        auto item1 = static_cast<uint64_t>(item);
91b1994897Sopenharmony_ci        uint32_t hash = PseudoFnvHashItem(static_cast<uint32_t>(item1), seed);
92b1994897Sopenharmony_ci        constexpr uint32_t FOUR_BYTES = 32U;
93b1994897Sopenharmony_ci        return PseudoFnvHashItem(static_cast<uint32_t>(item1 >> FOUR_BYTES), hash);
94b1994897Sopenharmony_ci    } else {
95b1994897Sopenharmony_ci        static_assert(sizeof(Item *) == 0, "PseudoFnvHashItem can only be called on types of size 1, 2, 4 or 8");
96b1994897Sopenharmony_ci    }
97b1994897Sopenharmony_ci}
98b1994897Sopenharmony_ci
99b1994897Sopenharmony_ci/// Works like FNV hash but operates over 4-byte words at a time instead of single bytes
100b1994897Sopenharmony_ciinline uint32_t PseudoFnvHashString(const uint8_t *str, uint32_t hash = FNV_INITIAL_SEED)
101b1994897Sopenharmony_ci{
102b1994897Sopenharmony_ci    while (true) {
103b1994897Sopenharmony_ci        // NOLINTNEXTLINE(readability-implicit-bool-conversion, cppcoreguidelines-pro-bounds-pointer-arithmetic)
104b1994897Sopenharmony_ci        // 0,1,2,3 is the index value, check whether the character with index 0,1,2,3
105b1994897Sopenharmony_ci        if (!str[0] || !str[1] || !str[2] || !str[3]) {
106b1994897Sopenharmony_ci            break;
107b1994897Sopenharmony_ci        }
108b1994897Sopenharmony_ci        constexpr uint32_t BYTE = 8U;
109b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
110b1994897Sopenharmony_ci        uint32_t word32 = str[0];
111b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
112b1994897Sopenharmony_ci        word32 |= static_cast<uint32_t>(str[1]) << BYTE;
113b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
114b1994897Sopenharmony_ci        word32 |= static_cast<uint32_t>(str[2U]) << (BYTE * 2U);
115b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
116b1994897Sopenharmony_ci        word32 |= static_cast<uint32_t>(str[3U]) << (BYTE * 3U);
117b1994897Sopenharmony_ci        hash = PseudoFnvHashItem(word32, hash);
118b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
119b1994897Sopenharmony_ci        str += sizeof(uint32_t);
120b1994897Sopenharmony_ci    }
121b1994897Sopenharmony_ci    while (*str != 0) {
122b1994897Sopenharmony_ci        hash = PseudoFnvHashItem(*str, hash);
123b1994897Sopenharmony_ci        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
124b1994897Sopenharmony_ci        ++str;
125b1994897Sopenharmony_ci    }
126b1994897Sopenharmony_ci    return hash;
127b1994897Sopenharmony_ci}
128b1994897Sopenharmony_ci
129b1994897Sopenharmony_ci// TODO(romanov,dyadov) Either rename to PseudoFnvHash or implement and call original FNV
130b1994897Sopenharmony_citemplate <typename Container>
131b1994897Sopenharmony_ciuint32_t FnvHash(const Container &data, uint32_t hash = FNV_INITIAL_SEED)
132b1994897Sopenharmony_ci{
133b1994897Sopenharmony_ci    for (const auto value : data) {
134b1994897Sopenharmony_ci        hash = PseudoFnvHashItem(value, hash);
135b1994897Sopenharmony_ci    }
136b1994897Sopenharmony_ci    return hash;
137b1994897Sopenharmony_ci}
138b1994897Sopenharmony_ci
139b1994897Sopenharmony_ci/**
140b1994897Sopenharmony_ci * Combine lhash and rhash
141b1994897Sopenharmony_ci */
142b1994897Sopenharmony_ciinline size_t merge_hashes(size_t lhash, size_t rhash)
143b1994897Sopenharmony_ci{
144b1994897Sopenharmony_ci    constexpr const size_t magic_constant = 0x9e3779b9;
145b1994897Sopenharmony_ci    size_t shl = lhash << 6U;
146b1994897Sopenharmony_ci    size_t shr = lhash >> 2U;
147b1994897Sopenharmony_ci    return lhash ^ (rhash + magic_constant + shl + shr);
148b1994897Sopenharmony_ci}
149b1994897Sopenharmony_ci
150b1994897Sopenharmony_ci/**
151b1994897Sopenharmony_ci * Combine lhash and rhash
152b1994897Sopenharmony_ci */
153b1994897Sopenharmony_ci// this overload is only enabled when it can't conflict with the size_t one
154b1994897Sopenharmony_citemplate <typename T = uint32_t, typename std::enable_if_t<(sizeof(size_t) != sizeof(T)), int> = 0>
155b1994897Sopenharmony_ciinline uint32_t merge_hashes(uint32_t lhash, uint32_t rhash)
156b1994897Sopenharmony_ci{
157b1994897Sopenharmony_ci    // note that the numbers are for 32 bits specifically, see https://github.com/HowardHinnant/hash_append/issues/7
158b1994897Sopenharmony_ci    constexpr const uint32_t magic_constant = 0x9e3779b9;
159b1994897Sopenharmony_ci    uint32_t shl = lhash << 6U;
160b1994897Sopenharmony_ci    uint32_t shr = lhash >> 2U;
161b1994897Sopenharmony_ci    return lhash ^ (rhash + magic_constant + shl + shr);
162b1994897Sopenharmony_ci}
163b1994897Sopenharmony_ci
164b1994897Sopenharmony_ci}  // namespace panda
165b1994897Sopenharmony_ci
166b1994897Sopenharmony_ci#endif  // LIBPANDABASE_UTILS_HASH_H
167