spy_hash_state.h 9.4 KB

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