1*b7893ccfSSadaf Ebrahimi /* Copyright (c) 2015-2016 The Khronos Group Inc.
2*b7893ccfSSadaf Ebrahimi * Copyright (c) 2015-2016 Valve Corporation
3*b7893ccfSSadaf Ebrahimi * Copyright (c) 2015-2016 LunarG, Inc.
4*b7893ccfSSadaf Ebrahimi * Copyright (C) 2015-2016 Google Inc.
5*b7893ccfSSadaf Ebrahimi *
6*b7893ccfSSadaf Ebrahimi * Licensed under the Apache License, Version 2.0 (the "License");
7*b7893ccfSSadaf Ebrahimi * you may not use this file except in compliance with the License.
8*b7893ccfSSadaf Ebrahimi * You may obtain a copy of the License at
9*b7893ccfSSadaf Ebrahimi *
10*b7893ccfSSadaf Ebrahimi * http://www.apache.org/licenses/LICENSE-2.0
11*b7893ccfSSadaf Ebrahimi *
12*b7893ccfSSadaf Ebrahimi * Unless required by applicable law or agreed to in writing, software
13*b7893ccfSSadaf Ebrahimi * distributed under the License is distributed on an "AS IS" BASIS,
14*b7893ccfSSadaf Ebrahimi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*b7893ccfSSadaf Ebrahimi * See the License for the specific language governing permissions and
16*b7893ccfSSadaf Ebrahimi * limitations under the License.
17*b7893ccfSSadaf Ebrahimi *
18*b7893ccfSSadaf Ebrahimi * Author: John Zulauf <[email protected]>
19*b7893ccfSSadaf Ebrahimi */
20*b7893ccfSSadaf Ebrahimi #ifndef HASH_UTIL_H_
21*b7893ccfSSadaf Ebrahimi #define HASH_UTIL_H_
22*b7893ccfSSadaf Ebrahimi
23*b7893ccfSSadaf Ebrahimi #define NOMINMAX
24*b7893ccfSSadaf Ebrahimi #include <cstdint>
25*b7893ccfSSadaf Ebrahimi #include <functional>
26*b7893ccfSSadaf Ebrahimi #include <limits>
27*b7893ccfSSadaf Ebrahimi #include <memory>
28*b7893ccfSSadaf Ebrahimi #include <mutex>
29*b7893ccfSSadaf Ebrahimi #include <type_traits>
30*b7893ccfSSadaf Ebrahimi #include <unordered_set>
31*b7893ccfSSadaf Ebrahimi #include <vector>
32*b7893ccfSSadaf Ebrahimi
33*b7893ccfSSadaf Ebrahimi // Hash and equality utilities for supporting hashing containers (e.g. unordered_set, unordered_map)
34*b7893ccfSSadaf Ebrahimi namespace hash_util {
35*b7893ccfSSadaf Ebrahimi
36*b7893ccfSSadaf Ebrahimi // True iff both pointers are null or both are non-null
37*b7893ccfSSadaf Ebrahimi template <typename T>
similar_for_nullity(const T * const lhs,const T * const rhs)38*b7893ccfSSadaf Ebrahimi bool similar_for_nullity(const T *const lhs, const T *const rhs) {
39*b7893ccfSSadaf Ebrahimi return ((lhs != nullptr) && (rhs != nullptr)) || ((lhs == nullptr) && (rhs == nullptr));
40*b7893ccfSSadaf Ebrahimi }
41*b7893ccfSSadaf Ebrahimi
42*b7893ccfSSadaf Ebrahimi // Wrap std hash to avoid manual casts for the holes in std::hash (in C++11)
43*b7893ccfSSadaf Ebrahimi template <typename Value>
44*b7893ccfSSadaf Ebrahimi size_t HashWithUnderlying(Value value, typename std::enable_if<!std::is_enum<Value>::value, void *>::type = nullptr) {
45*b7893ccfSSadaf Ebrahimi return std::hash<Value>()(value);
46*b7893ccfSSadaf Ebrahimi }
47*b7893ccfSSadaf Ebrahimi
48*b7893ccfSSadaf Ebrahimi template <typename Value>
49*b7893ccfSSadaf Ebrahimi size_t HashWithUnderlying(Value value, typename std::enable_if<std::is_enum<Value>::value, void *>::type = nullptr) {
50*b7893ccfSSadaf Ebrahimi using Underlying = typename std::underlying_type<Value>::type;
51*b7893ccfSSadaf Ebrahimi return std::hash<Underlying>()(static_cast<const Underlying &>(value));
52*b7893ccfSSadaf Ebrahimi }
53*b7893ccfSSadaf Ebrahimi
54*b7893ccfSSadaf Ebrahimi class HashCombiner {
55*b7893ccfSSadaf Ebrahimi public:
56*b7893ccfSSadaf Ebrahimi using Key = size_t;
57*b7893ccfSSadaf Ebrahimi
58*b7893ccfSSadaf Ebrahimi template <typename Value>
59*b7893ccfSSadaf Ebrahimi struct WrappedHash {
operatorWrappedHash60*b7893ccfSSadaf Ebrahimi size_t operator()(const Value &value) const { return HashWithUnderlying(value); }
61*b7893ccfSSadaf Ebrahimi };
62*b7893ccfSSadaf Ebrahimi
combined_(combined)63*b7893ccfSSadaf Ebrahimi HashCombiner(Key combined = 0) : combined_(combined) {}
64*b7893ccfSSadaf Ebrahimi // magic and combination algorithm based on boost::hash_combine
65*b7893ccfSSadaf Ebrahimi // http://www.boost.org/doc/libs/1_43_0/doc/html/hash/reference.html#boost.hash_combine
66*b7893ccfSSadaf Ebrahimi // Magic value is 2^size / ((1-sqrt(5)/2)
67*b7893ccfSSadaf Ebrahimi static const uint64_t kMagic = sizeof(Key) > 4 ? Key(0x9e3779b97f4a7c16UL) : Key(0x9e3779b9U);
68*b7893ccfSSadaf Ebrahimi
69*b7893ccfSSadaf Ebrahimi // If you need to override the default hash
70*b7893ccfSSadaf Ebrahimi template <typename Value, typename Hasher = WrappedHash<Value>>
Combine(const Value & value)71*b7893ccfSSadaf Ebrahimi HashCombiner &Combine(const Value &value) {
72*b7893ccfSSadaf Ebrahimi combined_ ^= Hasher()(value) + kMagic + (combined_ << 6) + (combined_ >> 2);
73*b7893ccfSSadaf Ebrahimi return *this;
74*b7893ccfSSadaf Ebrahimi }
75*b7893ccfSSadaf Ebrahimi
76*b7893ccfSSadaf Ebrahimi template <typename Iterator, typename Hasher = WrappedHash<typename std::iterator_traits<Iterator>::value_type>>
Combine(Iterator first,Iterator end)77*b7893ccfSSadaf Ebrahimi HashCombiner &Combine(Iterator first, Iterator end) {
78*b7893ccfSSadaf Ebrahimi using Value = typename std::iterator_traits<Iterator>::value_type;
79*b7893ccfSSadaf Ebrahimi auto current = first;
80*b7893ccfSSadaf Ebrahimi for (; current != end; ++current) {
81*b7893ccfSSadaf Ebrahimi Combine<Value, Hasher>(*current);
82*b7893ccfSSadaf Ebrahimi }
83*b7893ccfSSadaf Ebrahimi return *this;
84*b7893ccfSSadaf Ebrahimi }
85*b7893ccfSSadaf Ebrahimi
86*b7893ccfSSadaf Ebrahimi template <typename Value, typename Hasher = WrappedHash<Value>>
Combine(const std::vector<Value> & vector)87*b7893ccfSSadaf Ebrahimi HashCombiner &Combine(const std::vector<Value> &vector) {
88*b7893ccfSSadaf Ebrahimi return Combine(vector.cbegin(), vector.cend());
89*b7893ccfSSadaf Ebrahimi }
90*b7893ccfSSadaf Ebrahimi
91*b7893ccfSSadaf Ebrahimi template <typename Value>
92*b7893ccfSSadaf Ebrahimi HashCombiner &operator<<(const Value &value) {
93*b7893ccfSSadaf Ebrahimi return Combine(value);
94*b7893ccfSSadaf Ebrahimi }
95*b7893ccfSSadaf Ebrahimi
Value()96*b7893ccfSSadaf Ebrahimi Key Value() const { return combined_; }
97*b7893ccfSSadaf Ebrahimi void Reset(Key combined = 0) { combined_ = combined; }
98*b7893ccfSSadaf Ebrahimi
99*b7893ccfSSadaf Ebrahimi private:
100*b7893ccfSSadaf Ebrahimi Key combined_;
101*b7893ccfSSadaf Ebrahimi };
102*b7893ccfSSadaf Ebrahimi
103*b7893ccfSSadaf Ebrahimi // A template to inherit std::hash overloads from when T::hash() is defined
104*b7893ccfSSadaf Ebrahimi template <typename T>
105*b7893ccfSSadaf Ebrahimi struct HasHashMember {
operatorHasHashMember106*b7893ccfSSadaf Ebrahimi size_t operator()(const T &value) const { return value.hash(); }
107*b7893ccfSSadaf Ebrahimi };
108*b7893ccfSSadaf Ebrahimi
109*b7893ccfSSadaf Ebrahimi // A template to inherit std::hash overloads from when is an *ordered* constainer
110*b7893ccfSSadaf Ebrahimi template <typename T>
111*b7893ccfSSadaf Ebrahimi struct IsOrderedContainer {
operatorIsOrderedContainer112*b7893ccfSSadaf Ebrahimi size_t operator()(const T &value) const { return HashCombiner().Combine(value.cbegin(), value.cend()).Value(); }
113*b7893ccfSSadaf Ebrahimi };
114*b7893ccfSSadaf Ebrahimi
115*b7893ccfSSadaf Ebrahimi // The dictionary provides a way of referencing canonical/reference
116*b7893ccfSSadaf Ebrahimi // data by id, such that the id's are invariant with dictionary
117*b7893ccfSSadaf Ebrahimi // resize/insert and that no entries point to identical data. This
118*b7893ccfSSadaf Ebrahimi // approach uses the address of the unique data and as the unique
119*b7893ccfSSadaf Ebrahimi // ID for a give value of T.
120*b7893ccfSSadaf Ebrahimi //
121*b7893ccfSSadaf Ebrahimi // Note: This ID is unique for a given application execution, neither
122*b7893ccfSSadaf Ebrahimi // globally unique, invariant, nor repeatable from execution to
123*b7893ccfSSadaf Ebrahimi // execution.
124*b7893ccfSSadaf Ebrahimi //
125*b7893ccfSSadaf Ebrahimi // The entries of the dictionary are shared_pointers (the contents of
126*b7893ccfSSadaf Ebrahimi // which are invariant with resize/insert), with the hash and equality
127*b7893ccfSSadaf Ebrahimi // template arguments wrapped in a shared pointer dereferencing
128*b7893ccfSSadaf Ebrahimi // function object
129*b7893ccfSSadaf Ebrahimi template <typename T, typename Hasher = std::hash<T>, typename KeyEqual = std::equal_to<T>>
130*b7893ccfSSadaf Ebrahimi class Dictionary {
131*b7893ccfSSadaf Ebrahimi public:
132*b7893ccfSSadaf Ebrahimi using Def = T;
133*b7893ccfSSadaf Ebrahimi using Id = std::shared_ptr<const Def>;
134*b7893ccfSSadaf Ebrahimi
135*b7893ccfSSadaf Ebrahimi // Find the unique entry match the provided value, adding if needed
136*b7893ccfSSadaf Ebrahimi // TODO: segregate lookup from insert, using reader/write locks to reduce contention -- if needed
137*b7893ccfSSadaf Ebrahimi template <typename U = T>
look_up(U && value)138*b7893ccfSSadaf Ebrahimi Id look_up(U &&value) {
139*b7893ccfSSadaf Ebrahimi // We create an Id from the value, which will either be retained by dict (if new) or deleted on return (if extant)
140*b7893ccfSSadaf Ebrahimi Id from_input = std::make_shared<T>(std::forward<U>(value));
141*b7893ccfSSadaf Ebrahimi
142*b7893ccfSSadaf Ebrahimi // Insert takes care of the "unique" id part by rejecting the insert if a key matching by_value exists, but returning us
143*b7893ccfSSadaf Ebrahimi // the Id of the extant shared_pointer(id->def) instead.
144*b7893ccfSSadaf Ebrahimi // return the value of the Iterator from the <Iterator, bool> pair returned by insert
145*b7893ccfSSadaf Ebrahimi Guard g(lock); // Dict isn't thread safe, and use is presumed to be multi-threaded
146*b7893ccfSSadaf Ebrahimi return *dict.insert(from_input).first;
147*b7893ccfSSadaf Ebrahimi }
148*b7893ccfSSadaf Ebrahimi
149*b7893ccfSSadaf Ebrahimi private:
150*b7893ccfSSadaf Ebrahimi struct HashKeyValue {
operatorHashKeyValue151*b7893ccfSSadaf Ebrahimi size_t operator()(const Id &value) const { return Hasher()(*value); }
152*b7893ccfSSadaf Ebrahimi };
153*b7893ccfSSadaf Ebrahimi struct KeyValueEqual {
operatorKeyValueEqual154*b7893ccfSSadaf Ebrahimi bool operator()(const Id &lhs, const Id &rhs) const { return KeyEqual()(*lhs, *rhs); }
155*b7893ccfSSadaf Ebrahimi };
156*b7893ccfSSadaf Ebrahimi using Dict = std::unordered_set<Id, HashKeyValue, KeyValueEqual>;
157*b7893ccfSSadaf Ebrahimi using Lock = std::mutex;
158*b7893ccfSSadaf Ebrahimi using Guard = std::lock_guard<Lock>;
159*b7893ccfSSadaf Ebrahimi Lock lock;
160*b7893ccfSSadaf Ebrahimi Dict dict;
161*b7893ccfSSadaf Ebrahimi };
162*b7893ccfSSadaf Ebrahimi } // namespace hash_util
163*b7893ccfSSadaf Ebrahimi
164*b7893ccfSSadaf Ebrahimi #endif // HASH_UTILS_H_
165