spy_hash_state.h
Go to the documentation of this file.
00001 // Copyright 2018 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #ifndef ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
00016 #define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
00017 
00018 #include <ostream>
00019 #include <string>
00020 #include <vector>
00021 
00022 #include "absl/hash/hash.h"
00023 #include "absl/strings/match.h"
00024 #include "absl/strings/str_format.h"
00025 #include "absl/strings/str_join.h"
00026 
00027 namespace absl {
00028 namespace hash_internal {
00029 
00030 // SpyHashState is an implementation of the HashState API that simply
00031 // accumulates all input bytes in an internal buffer. This makes it useful
00032 // for testing AbslHashValue overloads (so long as they are templated on the
00033 // HashState parameter), since it can report the exact hash representation
00034 // that the AbslHashValue overload produces.
00035 //
00036 // Sample usage:
00037 // EXPECT_EQ(SpyHashState::combine(SpyHashState(), foo),
00038 //           SpyHashState::combine(SpyHashState(), bar));
00039 template <typename T>
00040 class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> {
00041  public:
00042   SpyHashStateImpl() : error_(std::make_shared<absl::optional<std::string>>()) {
00043     static_assert(std::is_void<T>::value, "");
00044   }
00045 
00046   // Move-only
00047   SpyHashStateImpl(const SpyHashStateImpl&) = delete;
00048   SpyHashStateImpl& operator=(const SpyHashStateImpl&) = delete;
00049 
00050   SpyHashStateImpl(SpyHashStateImpl&& other) noexcept {
00051     *this = std::move(other);
00052   }
00053 
00054   SpyHashStateImpl& operator=(SpyHashStateImpl&& other) noexcept {
00055     hash_representation_ = std::move(other.hash_representation_);
00056     error_ = other.error_;
00057     moved_from_ = other.moved_from_;
00058     other.moved_from_ = true;
00059     return *this;
00060   }
00061 
00062   template <typename U>
00063   SpyHashStateImpl(SpyHashStateImpl<U>&& other) {  // NOLINT
00064     hash_representation_ = std::move(other.hash_representation_);
00065     error_ = other.error_;
00066     moved_from_ = other.moved_from_;
00067     other.moved_from_ = true;
00068   }
00069 
00070   template <typename A, typename... Args>
00071   static SpyHashStateImpl combine(SpyHashStateImpl s, const A& a,
00072                                   const Args&... args) {
00073     // Pass an instance of SpyHashStateImpl<A> when trying to combine `A`. This
00074     // allows us to test that the user only uses this instance for combine calls
00075     // and does not call AbslHashValue directly.
00076     // See AbslHashValue implementation at the bottom.
00077     s = SpyHashStateImpl<A>::HashStateBase::combine(std::move(s), a);
00078     return SpyHashStateImpl::combine(std::move(s), args...);
00079   }
00080   static SpyHashStateImpl combine(SpyHashStateImpl s) {
00081     if (direct_absl_hash_value_error_) {
00082       *s.error_ = "AbslHashValue should not be invoked directly.";
00083     } else if (s.moved_from_) {
00084       *s.error_ = "Used moved-from instance of the hash state object.";
00085     }
00086     return s;
00087   }
00088 
00089   static void SetDirectAbslHashValueError() {
00090     direct_absl_hash_value_error_ = true;
00091   }
00092 
00093   // Two SpyHashStateImpl objects are equal if they hold equal hash
00094   // representations.
00095   friend bool operator==(const SpyHashStateImpl& lhs,
00096                          const SpyHashStateImpl& rhs) {
00097     return lhs.hash_representation_ == rhs.hash_representation_;
00098   }
00099 
00100   friend bool operator!=(const SpyHashStateImpl& lhs,
00101                          const SpyHashStateImpl& rhs) {
00102     return !(lhs == rhs);
00103   }
00104 
00105   enum class CompareResult {
00106     kEqual,
00107     kASuffixB,
00108     kBSuffixA,
00109     kUnequal,
00110   };
00111 
00112   static CompareResult Compare(const SpyHashStateImpl& a,
00113                                const SpyHashStateImpl& b) {
00114     const std::string a_flat = absl::StrJoin(a.hash_representation_, "");
00115     const std::string b_flat = absl::StrJoin(b.hash_representation_, "");
00116     if (a_flat == b_flat) return CompareResult::kEqual;
00117     if (absl::EndsWith(a_flat, b_flat)) return CompareResult::kBSuffixA;
00118     if (absl::EndsWith(b_flat, a_flat)) return CompareResult::kASuffixB;
00119     return CompareResult::kUnequal;
00120   }
00121 
00122   // operator<< prints the hash representation as a hex and ASCII dump, to
00123   // facilitate debugging.
00124   friend std::ostream& operator<<(std::ostream& out,
00125                                   const SpyHashStateImpl& hash_state) {
00126     out << "[\n";
00127     for (auto& s : hash_state.hash_representation_) {
00128       size_t offset = 0;
00129       for (char c : s) {
00130         if (offset % 16 == 0) {
00131           out << absl::StreamFormat("\n0x%04x: ", offset);
00132         }
00133         if (offset % 2 == 0) {
00134           out << " ";
00135         }
00136         out << absl::StreamFormat("%02x", c);
00137         ++offset;
00138       }
00139       out << "\n";
00140     }
00141     return out << "]";
00142   }
00143 
00144   // The base case of the combine recursion, which writes raw bytes into the
00145   // internal buffer.
00146   static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state,
00147                                              const unsigned char* begin,
00148                                              size_t size) {
00149     hash_state.hash_representation_.emplace_back(
00150         reinterpret_cast<const char*>(begin), size);
00151     return hash_state;
00152   }
00153 
00154   using SpyHashStateImpl::HashStateBase::combine_contiguous;
00155 
00156   absl::optional<std::string> error() const {
00157     if (moved_from_) {
00158       return "Returned a moved-from instance of the hash state object.";
00159     }
00160     return *error_;
00161   }
00162 
00163  private:
00164   template <typename U>
00165   friend class SpyHashStateImpl;
00166 
00167   // This is true if SpyHashStateImpl<T> has been passed to a call of
00168   // AbslHashValue with the wrong type. This detects that the user called
00169   // AbslHashValue directly (because the hash state type does not match).
00170   static bool direct_absl_hash_value_error_;
00171 
00172   std::vector<std::string> hash_representation_;
00173   // This is a shared_ptr because we want all instances of the particular
00174   // SpyHashState run to share the field. This way we can set the error for
00175   // use-after-move and all the copies will see it.
00176   std::shared_ptr<absl::optional<std::string>> error_;
00177   bool moved_from_ = false;
00178 };
00179 
00180 template <typename T>
00181 bool SpyHashStateImpl<T>::direct_absl_hash_value_error_;
00182 
00183 template <bool& B>
00184 struct OdrUse {
00185   constexpr OdrUse() {}
00186   bool& b = B;
00187 };
00188 
00189 template <void (*)()>
00190 struct RunOnStartup {
00191   static bool run;
00192   static constexpr OdrUse<run> kOdrUse{};
00193 };
00194 
00195 template <void (*f)()>
00196 bool RunOnStartup<f>::run = (f(), true);
00197 
00198 template <
00199     typename T, typename U,
00200     // Only trigger for when (T != U),
00201     typename = absl::enable_if_t<!std::is_same<T, U>::value>,
00202     // This statement works in two ways:
00203     //  - First, it instantiates RunOnStartup and forces the initialization of
00204     //    `run`, which set the global variable.
00205     //  - Second, it triggers a SFINAE error disabling the overload to prevent
00206     //    compile time errors. If we didn't disable the overload we would get
00207     //    ambiguous overload errors, which we don't want.
00208     int = RunOnStartup<SpyHashStateImpl<T>::SetDirectAbslHashValueError>::run>
00209 void AbslHashValue(SpyHashStateImpl<T>, const U&);
00210 
00211 using SpyHashState = SpyHashStateImpl<void>;
00212 
00213 }  // namespace hash_internal
00214 }  // namespace absl
00215 
00216 #endif  // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15