148fb7bfaSmrg // Definition of _Hash_bytes. -*- C++ -*- 248fb7bfaSmrg 3*b1e83836Smrg // Copyright (C) 2010-2022 Free Software Foundation, Inc. 448fb7bfaSmrg // 548fb7bfaSmrg // This file is part of the GNU ISO C++ Library. This library is free 648fb7bfaSmrg // software; you can redistribute it and/or modify it under the 748fb7bfaSmrg // terms of the GNU General Public License as published by the 848fb7bfaSmrg // Free Software Foundation; either version 3, or (at your option) 948fb7bfaSmrg // any later version. 1048fb7bfaSmrg 1148fb7bfaSmrg // This library is distributed in the hope that it will be useful, 1248fb7bfaSmrg // but WITHOUT ANY WARRANTY; without even the implied warranty of 1348fb7bfaSmrg // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1448fb7bfaSmrg // GNU General Public License for more details. 1548fb7bfaSmrg 1648fb7bfaSmrg // Under Section 7 of GPL version 3, you are granted additional 1748fb7bfaSmrg // permissions described in the GCC Runtime Library Exception, version 1848fb7bfaSmrg // 3.1, as published by the Free Software Foundation. 1948fb7bfaSmrg 2048fb7bfaSmrg // You should have received a copy of the GNU General Public License and 2148fb7bfaSmrg // a copy of the GCC Runtime Library Exception along with this program; 2248fb7bfaSmrg // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 2348fb7bfaSmrg // <http://www.gnu.org/licenses/>. 2448fb7bfaSmrg 2548fb7bfaSmrg // This file defines Hash_bytes, a primitive used for defining hash 2648fb7bfaSmrg // functions. Based on public domain MurmurHashUnaligned2, by Austin 2748fb7bfaSmrg // Appleby. http://murmurhash.googlepages.com/ 2848fb7bfaSmrg 2948fb7bfaSmrg // This file also defines _Fnv_hash_bytes, another primitive with 3048fb7bfaSmrg // exactly the same interface but using a different hash algorithm, 3148fb7bfaSmrg // Fowler / Noll / Vo (FNV) Hash (type FNV-1a). The Murmur hash 3248fb7bfaSmrg // function apears to be better in both speed and hash quality, and 3348fb7bfaSmrg // FNV is provided primarily for backward compatibility. 3448fb7bfaSmrg 3548fb7bfaSmrg #include <bits/hash_bytes.h> 3648fb7bfaSmrg 3748fb7bfaSmrg namespace 3848fb7bfaSmrg { 3948fb7bfaSmrg inline std::size_t unaligned_load(const char * p)4048fb7bfaSmrg unaligned_load(const char* p) 4148fb7bfaSmrg { 4248fb7bfaSmrg std::size_t result; 4348fb7bfaSmrg __builtin_memcpy(&result, p, sizeof(result)); 4448fb7bfaSmrg return result; 4548fb7bfaSmrg } 4648fb7bfaSmrg 4748fb7bfaSmrg #if __SIZEOF_SIZE_T__ == 8 4848fb7bfaSmrg // Loads n bytes, where 1 <= n < 8. 4948fb7bfaSmrg inline std::size_t load_bytes(const char * p,int n)5048fb7bfaSmrg load_bytes(const char* p, int n) 5148fb7bfaSmrg { 5248fb7bfaSmrg std::size_t result = 0; 5348fb7bfaSmrg --n; 5448fb7bfaSmrg do 5548fb7bfaSmrg result = (result << 8) + static_cast<unsigned char>(p[n]); 5648fb7bfaSmrg while (--n >= 0); 5748fb7bfaSmrg return result; 5848fb7bfaSmrg } 5948fb7bfaSmrg 6048fb7bfaSmrg inline std::size_t shift_mix(std::size_t v)6148fb7bfaSmrg shift_mix(std::size_t v) 6248fb7bfaSmrg { return v ^ (v >> 47);} 6348fb7bfaSmrg #endif 6448fb7bfaSmrg } 6548fb7bfaSmrg 6648fb7bfaSmrg namespace std 6748fb7bfaSmrg { 6848fb7bfaSmrg _GLIBCXX_BEGIN_NAMESPACE_VERSION 6948fb7bfaSmrg 7048fb7bfaSmrg #if __SIZEOF_SIZE_T__ == 4 7148fb7bfaSmrg 7248fb7bfaSmrg // Implementation of Murmur hash for 32-bit size_t. 7348fb7bfaSmrg size_t _Hash_bytes(const void * ptr,size_t len,size_t seed)7448fb7bfaSmrg _Hash_bytes(const void* ptr, size_t len, size_t seed) 7548fb7bfaSmrg { 7648fb7bfaSmrg const size_t m = 0x5bd1e995; 7748fb7bfaSmrg size_t hash = seed ^ len; 7848fb7bfaSmrg const char* buf = static_cast<const char*>(ptr); 7948fb7bfaSmrg 8048fb7bfaSmrg // Mix 4 bytes at a time into the hash. 8148fb7bfaSmrg while(len >= 4) 8248fb7bfaSmrg { 8348fb7bfaSmrg size_t k = unaligned_load(buf); 8448fb7bfaSmrg k *= m; 8548fb7bfaSmrg k ^= k >> 24; 8648fb7bfaSmrg k *= m; 8748fb7bfaSmrg hash *= m; 8848fb7bfaSmrg hash ^= k; 8948fb7bfaSmrg buf += 4; 9048fb7bfaSmrg len -= 4; 9148fb7bfaSmrg } 9248fb7bfaSmrg 9348fb7bfaSmrg // Handle the last few bytes of the input array. 9448fb7bfaSmrg switch(len) 9548fb7bfaSmrg { 9648fb7bfaSmrg case 3: 9748fb7bfaSmrg hash ^= static_cast<unsigned char>(buf[2]) << 16; 98b17d1066Smrg [[gnu::fallthrough]]; 9948fb7bfaSmrg case 2: 10048fb7bfaSmrg hash ^= static_cast<unsigned char>(buf[1]) << 8; 101b17d1066Smrg [[gnu::fallthrough]]; 10248fb7bfaSmrg case 1: 10348fb7bfaSmrg hash ^= static_cast<unsigned char>(buf[0]); 10448fb7bfaSmrg hash *= m; 10548fb7bfaSmrg }; 10648fb7bfaSmrg 10748fb7bfaSmrg // Do a few final mixes of the hash. 10848fb7bfaSmrg hash ^= hash >> 13; 10948fb7bfaSmrg hash *= m; 11048fb7bfaSmrg hash ^= hash >> 15; 11148fb7bfaSmrg return hash; 11248fb7bfaSmrg } 11348fb7bfaSmrg 11448fb7bfaSmrg // Implementation of FNV hash for 32-bit size_t. 115b17d1066Smrg // N.B. This function should work on unsigned char, otherwise it does not 116b17d1066Smrg // correctly implement the FNV-1a algorithm (see PR59406). 117b17d1066Smrg // The existing behaviour is retained for backwards compatibility. 11848fb7bfaSmrg size_t _Fnv_hash_bytes(const void * ptr,size_t len,size_t hash)11948fb7bfaSmrg _Fnv_hash_bytes(const void* ptr, size_t len, size_t hash) 12048fb7bfaSmrg { 12148fb7bfaSmrg const char* cptr = static_cast<const char*>(ptr); 12248fb7bfaSmrg for (; len; --len) 12348fb7bfaSmrg { 12448fb7bfaSmrg hash ^= static_cast<size_t>(*cptr++); 12548fb7bfaSmrg hash *= static_cast<size_t>(16777619UL); 12648fb7bfaSmrg } 12748fb7bfaSmrg return hash; 12848fb7bfaSmrg } 12948fb7bfaSmrg 13048fb7bfaSmrg #elif __SIZEOF_SIZE_T__ == 8 13148fb7bfaSmrg 13248fb7bfaSmrg // Implementation of Murmur hash for 64-bit size_t. 13348fb7bfaSmrg size_t 13448fb7bfaSmrg _Hash_bytes(const void* ptr, size_t len, size_t seed) 13548fb7bfaSmrg { 13648fb7bfaSmrg static const size_t mul = (((size_t) 0xc6a4a793UL) << 32UL) 13748fb7bfaSmrg + (size_t) 0x5bd1e995UL; 13848fb7bfaSmrg const char* const buf = static_cast<const char*>(ptr); 13948fb7bfaSmrg 14048fb7bfaSmrg // Remove the bytes not divisible by the sizeof(size_t). This 14148fb7bfaSmrg // allows the main loop to process the data as 64-bit integers. 142003ba354Smrg const size_t len_aligned = len & ~(size_t)0x7; 14348fb7bfaSmrg const char* const end = buf + len_aligned; 14448fb7bfaSmrg size_t hash = seed ^ (len * mul); 14548fb7bfaSmrg for (const char* p = buf; p != end; p += 8) 14648fb7bfaSmrg { 14748fb7bfaSmrg const size_t data = shift_mix(unaligned_load(p) * mul) * mul; 14848fb7bfaSmrg hash ^= data; 14948fb7bfaSmrg hash *= mul; 15048fb7bfaSmrg } 15148fb7bfaSmrg if ((len & 0x7) != 0) 15248fb7bfaSmrg { 15348fb7bfaSmrg const size_t data = load_bytes(end, len & 0x7); 15448fb7bfaSmrg hash ^= data; 15548fb7bfaSmrg hash *= mul; 15648fb7bfaSmrg } 15748fb7bfaSmrg hash = shift_mix(hash) * mul; 15848fb7bfaSmrg hash = shift_mix(hash); 15948fb7bfaSmrg return hash; 16048fb7bfaSmrg } 16148fb7bfaSmrg 16248fb7bfaSmrg // Implementation of FNV hash for 64-bit size_t. 163b17d1066Smrg // N.B. This function should work on unsigned char, otherwise it does not 164b17d1066Smrg // correctly implement the FNV-1a algorithm (see PR59406). 165b17d1066Smrg // The existing behaviour is retained for backwards compatibility. 16648fb7bfaSmrg size_t 16748fb7bfaSmrg _Fnv_hash_bytes(const void* ptr, size_t len, size_t hash) 16848fb7bfaSmrg { 16948fb7bfaSmrg const char* cptr = static_cast<const char*>(ptr); 17048fb7bfaSmrg for (; len; --len) 17148fb7bfaSmrg { 17248fb7bfaSmrg hash ^= static_cast<size_t>(*cptr++); 17348fb7bfaSmrg hash *= static_cast<size_t>(1099511628211ULL); 17448fb7bfaSmrg } 17548fb7bfaSmrg return hash; 17648fb7bfaSmrg } 17748fb7bfaSmrg 17848fb7bfaSmrg #else 17948fb7bfaSmrg 18048fb7bfaSmrg // Dummy hash implementation for unusual sizeof(size_t). 18148fb7bfaSmrg size_t 18248fb7bfaSmrg _Hash_bytes(const void* ptr, size_t len, size_t seed) 18348fb7bfaSmrg { 18448fb7bfaSmrg size_t hash = seed; 18548fb7bfaSmrg const char* cptr = reinterpret_cast<const char*>(ptr); 18648fb7bfaSmrg for (; len; --len) 18748fb7bfaSmrg hash = (hash * 131) + *cptr++; 18848fb7bfaSmrg return hash; 18948fb7bfaSmrg } 19048fb7bfaSmrg 19148fb7bfaSmrg size_t 19248fb7bfaSmrg _Fnv_hash_bytes(const void* ptr, size_t len, size_t seed) 19348fb7bfaSmrg { return _Hash_bytes(ptr, len, seed); } 19448fb7bfaSmrg 19548fb7bfaSmrg #endif /* __SIZEOF_SIZE_T__ */ 19648fb7bfaSmrg 19748fb7bfaSmrg _GLIBCXX_END_NAMESPACE_VERSION 19848fb7bfaSmrg } // namespace 199