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