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