xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/libsupc++/hash_bytes.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
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