1 // Copyright 2018 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ 16 #define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ 17 18 #include <ostream> 19 #include <string> 20 #include <vector> 21 22 #include "absl/hash/hash.h" 23 #include "absl/strings/match.h" 24 #include "absl/strings/str_format.h" 25 #include "absl/strings/str_join.h" 26 27 namespace absl { 28 ABSL_NAMESPACE_BEGIN 29 namespace hash_internal { 30 31 // SpyHashState is an implementation of the HashState API that simply 32 // accumulates all input bytes in an internal buffer. This makes it useful 33 // for testing AbslHashValue overloads (so long as they are templated on the 34 // HashState parameter), since it can report the exact hash representation 35 // that the AbslHashValue overload produces. 36 // 37 // Sample usage: 38 // EXPECT_EQ(SpyHashState::combine(SpyHashState(), foo), 39 // SpyHashState::combine(SpyHashState(), bar)); 40 template <typename T> 41 class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> { 42 public: SpyHashStateImpl()43 SpyHashStateImpl() : error_(std::make_shared<absl::optional<std::string>>()) { 44 static_assert(std::is_void<T>::value, ""); 45 } 46 47 // Move-only 48 SpyHashStateImpl(const SpyHashStateImpl&) = delete; 49 SpyHashStateImpl& operator=(const SpyHashStateImpl&) = delete; 50 SpyHashStateImpl(SpyHashStateImpl && other)51 SpyHashStateImpl(SpyHashStateImpl&& other) noexcept { 52 *this = std::move(other); 53 } 54 55 SpyHashStateImpl& operator=(SpyHashStateImpl&& other) noexcept { 56 hash_representation_ = std::move(other.hash_representation_); 57 error_ = other.error_; 58 moved_from_ = other.moved_from_; 59 other.moved_from_ = true; 60 return *this; 61 } 62 63 template <typename U> SpyHashStateImpl(SpyHashStateImpl<U> && other)64 SpyHashStateImpl(SpyHashStateImpl<U>&& other) { // NOLINT 65 hash_representation_ = std::move(other.hash_representation_); 66 error_ = other.error_; 67 moved_from_ = other.moved_from_; 68 other.moved_from_ = true; 69 } 70 71 template <typename A, typename... Args> combine(SpyHashStateImpl s,const A & a,const Args &...args)72 static SpyHashStateImpl combine(SpyHashStateImpl s, const A& a, 73 const Args&... args) { 74 // Pass an instance of SpyHashStateImpl<A> when trying to combine `A`. This 75 // allows us to test that the user only uses this instance for combine calls 76 // and does not call AbslHashValue directly. 77 // See AbslHashValue implementation at the bottom. 78 s = SpyHashStateImpl<A>::HashStateBase::combine(std::move(s), a); 79 return SpyHashStateImpl::combine(std::move(s), args...); 80 } combine(SpyHashStateImpl s)81 static SpyHashStateImpl combine(SpyHashStateImpl s) { 82 if (direct_absl_hash_value_error_) { 83 *s.error_ = "AbslHashValue should not be invoked directly."; 84 } else if (s.moved_from_) { 85 *s.error_ = "Used moved-from instance of the hash state object."; 86 } 87 return s; 88 } 89 SetDirectAbslHashValueError()90 static void SetDirectAbslHashValueError() { 91 direct_absl_hash_value_error_ = true; 92 } 93 94 // Two SpyHashStateImpl objects are equal if they hold equal hash 95 // representations. 96 friend bool operator==(const SpyHashStateImpl& lhs, 97 const SpyHashStateImpl& rhs) { 98 return lhs.hash_representation_ == rhs.hash_representation_; 99 } 100 101 friend bool operator!=(const SpyHashStateImpl& lhs, 102 const SpyHashStateImpl& rhs) { 103 return !(lhs == rhs); 104 } 105 106 enum class CompareResult { 107 kEqual, 108 kASuffixB, 109 kBSuffixA, 110 kUnequal, 111 }; 112 Compare(const SpyHashStateImpl & a,const SpyHashStateImpl & b)113 static CompareResult Compare(const SpyHashStateImpl& a, 114 const SpyHashStateImpl& b) { 115 const std::string a_flat = absl::StrJoin(a.hash_representation_, ""); 116 const std::string b_flat = absl::StrJoin(b.hash_representation_, ""); 117 if (a_flat == b_flat) return CompareResult::kEqual; 118 if (absl::EndsWith(a_flat, b_flat)) return CompareResult::kBSuffixA; 119 if (absl::EndsWith(b_flat, a_flat)) return CompareResult::kASuffixB; 120 return CompareResult::kUnequal; 121 } 122 123 // operator<< prints the hash representation as a hex and ASCII dump, to 124 // facilitate debugging. 125 friend std::ostream& operator<<(std::ostream& out, 126 const SpyHashStateImpl& hash_state) { 127 out << "[\n"; 128 for (auto& s : hash_state.hash_representation_) { 129 size_t offset = 0; 130 for (char c : s) { 131 if (offset % 16 == 0) { 132 out << absl::StreamFormat("\n0x%04x: ", offset); 133 } 134 if (offset % 2 == 0) { 135 out << " "; 136 } 137 out << absl::StreamFormat("%02x", c); 138 ++offset; 139 } 140 out << "\n"; 141 } 142 return out << "]"; 143 } 144 145 // The base case of the combine recursion, which writes raw bytes into the 146 // internal buffer. combine_contiguous(SpyHashStateImpl hash_state,const unsigned char * begin,size_t size)147 static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state, 148 const unsigned char* begin, 149 size_t size) { 150 const size_t large_chunk_stride = PiecewiseChunkSize(); 151 if (size > large_chunk_stride) { 152 // Combining a large contiguous buffer must have the same effect as 153 // doing it piecewise by the stride length, followed by the (possibly 154 // empty) remainder. 155 while (size >= large_chunk_stride) { 156 hash_state = SpyHashStateImpl::combine_contiguous( 157 std::move(hash_state), begin, large_chunk_stride); 158 begin += large_chunk_stride; 159 size -= large_chunk_stride; 160 } 161 } 162 163 hash_state.hash_representation_.emplace_back( 164 reinterpret_cast<const char*>(begin), size); 165 return hash_state; 166 } 167 168 using SpyHashStateImpl::HashStateBase::combine_contiguous; 169 error()170 absl::optional<std::string> error() const { 171 if (moved_from_) { 172 return "Returned a moved-from instance of the hash state object."; 173 } 174 return *error_; 175 } 176 177 private: 178 template <typename U> 179 friend class SpyHashStateImpl; 180 181 // This is true if SpyHashStateImpl<T> has been passed to a call of 182 // AbslHashValue with the wrong type. This detects that the user called 183 // AbslHashValue directly (because the hash state type does not match). 184 static bool direct_absl_hash_value_error_; 185 186 std::vector<std::string> hash_representation_; 187 // This is a shared_ptr because we want all instances of the particular 188 // SpyHashState run to share the field. This way we can set the error for 189 // use-after-move and all the copies will see it. 190 std::shared_ptr<absl::optional<std::string>> error_; 191 bool moved_from_ = false; 192 }; 193 194 template <typename T> 195 bool SpyHashStateImpl<T>::direct_absl_hash_value_error_; 196 197 template <bool& B> 198 struct OdrUse { OdrUseOdrUse199 constexpr OdrUse() {} 200 bool& b = B; 201 }; 202 203 template <void (*)()> 204 struct RunOnStartup { 205 static bool run; 206 static constexpr OdrUse<run> kOdrUse{}; 207 }; 208 209 template <void (*f)()> 210 bool RunOnStartup<f>::run = (f(), true); 211 212 template < 213 typename T, typename U, 214 // Only trigger for when (T != U), 215 typename = absl::enable_if_t<!std::is_same<T, U>::value>, 216 // This statement works in two ways: 217 // - First, it instantiates RunOnStartup and forces the initialization of 218 // `run`, which set the global variable. 219 // - Second, it triggers a SFINAE error disabling the overload to prevent 220 // compile time errors. If we didn't disable the overload we would get 221 // ambiguous overload errors, which we don't want. 222 int = RunOnStartup<SpyHashStateImpl<T>::SetDirectAbslHashValueError>::run> 223 void AbslHashValue(SpyHashStateImpl<T>, const U&); 224 225 using SpyHashState = SpyHashStateImpl<void>; 226 227 } // namespace hash_internal 228 ABSL_NAMESPACE_END 229 } // namespace absl 230 231 #endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ 232