// Copyright 2018 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef COMMON_HASHUTILS_H_ #define COMMON_HASHUTILS_H_ #include "common/Platform.h" #include "common/TypedInteger.h" #include "common/ityp_bitset.h" #include #include // Wrapper around std::hash to make it a templated function instead of a functor. It is marginally // nicer, and avoids adding to the std namespace to add hashing of other types. template size_t Hash(const T& value) { return std::hash()(value); } // Add hashing of TypedIntegers template size_t Hash(const TypedInteger& value) { return Hash(static_cast(value)); } // When hashing sparse structures we want to iteratively build a hash value with only parts of the // data. HashCombine "hashes" together an existing hash and hashable values. // // Example usage to compute the hash of a mask and values corresponding to the mask: // // size_t hash = Hash(mask): // for (uint32_t i : IterateBitSet(mask)) { HashCombine(&hash, hashables[i]); } // return hash; template void HashCombine(size_t* hash, const T& value) { #if defined(DAWN_PLATFORM_64_BIT) const size_t offset = 0x9e3779b97f4a7c16; #elif defined(DAWN_PLATFORM_32_BIT) const size_t offset = 0x9e3779b9; #else # error "Unsupported platform" #endif *hash ^= Hash(value) + offset + (*hash << 6) + (*hash >> 2); } template void HashCombine(size_t* hash, const T& value, const Args&... args) { HashCombine(hash, value); HashCombine(hash, args...); } // Workaround a bug between clang++ and libstdlibc++ by defining our own hashing for bitsets. // When _GLIBCXX_DEBUG is enabled libstdc++ wraps containers into debug containers. For bitset this // means what is normally std::bitset is defined as std::__cxx1988::bitset and is replaced by the // debug version of bitset. // When hashing, std::hash proxies the call to std::hash and // fails on clang because the latter tries to access the private _M_getdata member of the bitset. // It looks like it should work because the non-debug bitset declares // // friend struct std::hash // bitset is the name of the class itself // // which should friend std::hash but somehow doesn't work on clang. #if defined(_GLIBCXX_DEBUG) template size_t Hash(const std::bitset& value) { constexpr size_t kWindowSize = sizeof(unsigned long long); std::bitset bits = value; size_t hash = 0; for (size_t processedBits = 0; processedBits < N; processedBits += kWindowSize) { HashCombine(&hash, bits.to_ullong()); bits >>= kWindowSize; } return hash; } #endif namespace std { template class hash> { public: size_t operator()(const ityp::bitset& value) const { return Hash(static_cast&>(value)); } }; } // namespace std #endif // COMMON_HASHUTILS_H_