Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:27:18

0001 // Copyright 2018 The Abseil Authors.
0002 //
0003 // Licensed under the Apache License, Version 2.0 (the "License");
0004 // you may not use this file except in compliance with the License.
0005 // You may obtain a copy of the License at
0006 //
0007 //      https://www.apache.org/licenses/LICENSE-2.0
0008 //
0009 // Unless required by applicable law or agreed to in writing, software
0010 // distributed under the License is distributed on an "AS IS" BASIS,
0011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012 // See the License for the specific language governing permissions and
0013 // limitations under the License.
0014 
0015 #ifndef ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
0016 #define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
0017 
0018 #include <algorithm>
0019 #include <ostream>
0020 #include <string>
0021 #include <vector>
0022 
0023 #include "absl/hash/hash.h"
0024 #include "absl/strings/match.h"
0025 #include "absl/strings/str_format.h"
0026 #include "absl/strings/str_join.h"
0027 
0028 namespace absl {
0029 ABSL_NAMESPACE_BEGIN
0030 namespace hash_internal {
0031 
0032 // SpyHashState is an implementation of the HashState API that simply
0033 // accumulates all input bytes in an internal buffer. This makes it useful
0034 // for testing AbslHashValue overloads (so long as they are templated on the
0035 // HashState parameter), since it can report the exact hash representation
0036 // that the AbslHashValue overload produces.
0037 //
0038 // Sample usage:
0039 // EXPECT_EQ(SpyHashState::combine(SpyHashState(), foo),
0040 //           SpyHashState::combine(SpyHashState(), bar));
0041 template <typename T>
0042 class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> {
0043  public:
0044   SpyHashStateImpl() : error_(std::make_shared<absl::optional<std::string>>()) {
0045     static_assert(std::is_void<T>::value, "");
0046   }
0047 
0048   // Move-only
0049   SpyHashStateImpl(const SpyHashStateImpl&) = delete;
0050   SpyHashStateImpl& operator=(const SpyHashStateImpl&) = delete;
0051 
0052   SpyHashStateImpl(SpyHashStateImpl&& other) noexcept {
0053     *this = std::move(other);
0054   }
0055 
0056   SpyHashStateImpl& operator=(SpyHashStateImpl&& other) noexcept {
0057     hash_representation_ = std::move(other.hash_representation_);
0058     error_ = other.error_;
0059     moved_from_ = other.moved_from_;
0060     other.moved_from_ = true;
0061     return *this;
0062   }
0063 
0064   template <typename U>
0065   SpyHashStateImpl(SpyHashStateImpl<U>&& other) {  // NOLINT
0066     hash_representation_ = std::move(other.hash_representation_);
0067     error_ = other.error_;
0068     moved_from_ = other.moved_from_;
0069     other.moved_from_ = true;
0070   }
0071 
0072   template <typename A, typename... Args>
0073   static SpyHashStateImpl combine(SpyHashStateImpl s, const A& a,
0074                                   const Args&... args) {
0075     // Pass an instance of SpyHashStateImpl<A> when trying to combine `A`. This
0076     // allows us to test that the user only uses this instance for combine calls
0077     // and does not call AbslHashValue directly.
0078     // See AbslHashValue implementation at the bottom.
0079     s = SpyHashStateImpl<A>::HashStateBase::combine(std::move(s), a);
0080     return SpyHashStateImpl::combine(std::move(s), args...);
0081   }
0082   static SpyHashStateImpl combine(SpyHashStateImpl s) {
0083     if (direct_absl_hash_value_error_) {
0084       *s.error_ = "AbslHashValue should not be invoked directly.";
0085     } else if (s.moved_from_) {
0086       *s.error_ = "Used moved-from instance of the hash state object.";
0087     }
0088     return s;
0089   }
0090 
0091   static void SetDirectAbslHashValueError() {
0092     direct_absl_hash_value_error_ = true;
0093   }
0094 
0095   // Two SpyHashStateImpl objects are equal if they hold equal hash
0096   // representations.
0097   friend bool operator==(const SpyHashStateImpl& lhs,
0098                          const SpyHashStateImpl& rhs) {
0099     return lhs.hash_representation_ == rhs.hash_representation_;
0100   }
0101 
0102   friend bool operator!=(const SpyHashStateImpl& lhs,
0103                          const SpyHashStateImpl& rhs) {
0104     return !(lhs == rhs);
0105   }
0106 
0107   enum class CompareResult {
0108     kEqual,
0109     kASuffixB,
0110     kBSuffixA,
0111     kUnequal,
0112   };
0113 
0114   static CompareResult Compare(const SpyHashStateImpl& a,
0115                                const SpyHashStateImpl& b) {
0116     const std::string a_flat = absl::StrJoin(a.hash_representation_, "");
0117     const std::string b_flat = absl::StrJoin(b.hash_representation_, "");
0118     if (a_flat == b_flat) return CompareResult::kEqual;
0119     if (absl::EndsWith(a_flat, b_flat)) return CompareResult::kBSuffixA;
0120     if (absl::EndsWith(b_flat, a_flat)) return CompareResult::kASuffixB;
0121     return CompareResult::kUnequal;
0122   }
0123 
0124   // operator<< prints the hash representation as a hex and ASCII dump, to
0125   // facilitate debugging.
0126   friend std::ostream& operator<<(std::ostream& out,
0127                                   const SpyHashStateImpl& hash_state) {
0128     out << "[\n";
0129     for (auto& s : hash_state.hash_representation_) {
0130       size_t offset = 0;
0131       for (char c : s) {
0132         if (offset % 16 == 0) {
0133           out << absl::StreamFormat("\n0x%04x: ", offset);
0134         }
0135         if (offset % 2 == 0) {
0136           out << " ";
0137         }
0138         out << absl::StreamFormat("%02x", c);
0139         ++offset;
0140       }
0141       out << "\n";
0142     }
0143     return out << "]";
0144   }
0145 
0146   // The base case of the combine recursion, which writes raw bytes into the
0147   // internal buffer.
0148   static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state,
0149                                              const unsigned char* begin,
0150                                              size_t size) {
0151     const size_t large_chunk_stride = PiecewiseChunkSize();
0152     // Combining a large contiguous buffer must have the same effect as
0153     // doing it piecewise by the stride length, followed by the (possibly
0154     // empty) remainder.
0155     while (size > large_chunk_stride) {
0156       hash_state = SpyHashStateImpl::combine_contiguous(
0157           std::move(hash_state), begin, large_chunk_stride);
0158       begin += large_chunk_stride;
0159       size -= large_chunk_stride;
0160     }
0161 
0162     if (size > 0) {
0163       hash_state.hash_representation_.emplace_back(
0164           reinterpret_cast<const char*>(begin), size);
0165     }
0166     return hash_state;
0167   }
0168 
0169   using SpyHashStateImpl::HashStateBase::combine_contiguous;
0170 
0171   template <typename CombinerT>
0172   static SpyHashStateImpl RunCombineUnordered(SpyHashStateImpl state,
0173                                               CombinerT combiner) {
0174     UnorderedCombinerCallback cb;
0175 
0176     combiner(SpyHashStateImpl<void>{}, std::ref(cb));
0177 
0178     std::sort(cb.element_hash_representations.begin(),
0179               cb.element_hash_representations.end());
0180     state.hash_representation_.insert(state.hash_representation_.end(),
0181                                       cb.element_hash_representations.begin(),
0182                                       cb.element_hash_representations.end());
0183     if (cb.error && cb.error->has_value()) {
0184       state.error_ = std::move(cb.error);
0185     }
0186     return state;
0187   }
0188 
0189   absl::optional<std::string> error() const {
0190     if (moved_from_) {
0191       return "Returned a moved-from instance of the hash state object.";
0192     }
0193     return *error_;
0194   }
0195 
0196  private:
0197   template <typename U>
0198   friend class SpyHashStateImpl;
0199 
0200   struct UnorderedCombinerCallback {
0201     std::vector<std::string> element_hash_representations;
0202     std::shared_ptr<absl::optional<std::string>> error;
0203 
0204     // The inner spy can have a different type.
0205     template <typename U>
0206     void operator()(SpyHashStateImpl<U>& inner) {
0207       element_hash_representations.push_back(
0208           absl::StrJoin(inner.hash_representation_, ""));
0209       if (inner.error_->has_value()) {
0210         error = std::move(inner.error_);
0211       }
0212       inner = SpyHashStateImpl<void>{};
0213     }
0214   };
0215 
0216   // This is true if SpyHashStateImpl<T> has been passed to a call of
0217   // AbslHashValue with the wrong type. This detects that the user called
0218   // AbslHashValue directly (because the hash state type does not match).
0219   static bool direct_absl_hash_value_error_;
0220 
0221   std::vector<std::string> hash_representation_;
0222   // This is a shared_ptr because we want all instances of the particular
0223   // SpyHashState run to share the field. This way we can set the error for
0224   // use-after-move and all the copies will see it.
0225   std::shared_ptr<absl::optional<std::string>> error_;
0226   bool moved_from_ = false;
0227 };
0228 
0229 template <typename T>
0230 bool SpyHashStateImpl<T>::direct_absl_hash_value_error_;
0231 
0232 template <bool& B>
0233 struct OdrUse {
0234   constexpr OdrUse() {}
0235   bool& b = B;
0236 };
0237 
0238 template <void (*)()>
0239 struct RunOnStartup {
0240   static bool run;
0241   static constexpr OdrUse<run> kOdrUse{};
0242 };
0243 
0244 template <void (*f)()>
0245 bool RunOnStartup<f>::run = (f(), true);
0246 
0247 template <
0248     typename T, typename U,
0249     // Only trigger for when (T != U),
0250     typename = absl::enable_if_t<!std::is_same<T, U>::value>,
0251     // This statement works in two ways:
0252     //  - First, it instantiates RunOnStartup and forces the initialization of
0253     //    `run`, which set the global variable.
0254     //  - Second, it triggers a SFINAE error disabling the overload to prevent
0255     //    compile time errors. If we didn't disable the overload we would get
0256     //    ambiguous overload errors, which we don't want.
0257     int = RunOnStartup<SpyHashStateImpl<T>::SetDirectAbslHashValueError>::run>
0258 void AbslHashValue(SpyHashStateImpl<T>, const U&);
0259 
0260 using SpyHashState = SpyHashStateImpl<void>;
0261 
0262 }  // namespace hash_internal
0263 ABSL_NAMESPACE_END
0264 }  // namespace absl
0265 
0266 #endif  // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_