Export of internal Abseil changes.
-- 4eacae3ff1b14b1d309e8092185bc10e8a6203cf by Derek Mauro <dmauro@google.com>: Release SwissTable - a fast, efficient, cache-friendly hash table. https://www.youtube.com/watch?v=ncHmEUmJZf4 PiperOrigin-RevId: 214816527 -- df8c3dfab3cfb2f4365909a84d0683b193cfbb11 by Derek Mauro <dmauro@google.com>: Internal change PiperOrigin-RevId: 214785288 -- 1eabd5266bbcebc33eecc91e5309b751856a75c8 by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 214722931 -- 2ebbfac950f83146b46253038e7dd7dcde9f2951 by Derek Mauro <dmauro@google.com>: Internal change PiperOrigin-RevId: 214701684 GitOrigin-RevId: 4eacae3ff1b14b1d309e8092185bc10e8a6203cf Change-Id: I9ba64e395b22ad7863213d157b8019b082adc19d
This commit is contained in:
parent
e291c279e4
commit
48cd2c3f35
55 changed files with 18696 additions and 0 deletions
218
absl/hash/internal/spy_hash_state.h
Normal file
218
absl/hash/internal/spy_hash_state.h
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
|
||||
#define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/hash/hash.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
|
||||
namespace absl {
|
||||
namespace hash_internal {
|
||||
|
||||
// SpyHashState is an implementation of the HashState API that simply
|
||||
// accumulates all input bytes in an internal buffer. This makes it useful
|
||||
// for testing AbslHashValue overloads (so long as they are templated on the
|
||||
// HashState parameter), since it can report the exact hash representation
|
||||
// that the AbslHashValue overload produces.
|
||||
//
|
||||
// Sample usage:
|
||||
// EXPECT_EQ(SpyHashState::combine(SpyHashState(), foo),
|
||||
// SpyHashState::combine(SpyHashState(), bar));
|
||||
template <typename T>
|
||||
class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> {
|
||||
public:
|
||||
SpyHashStateImpl()
|
||||
: error_(std::make_shared<absl::optional<std::string>>()) {
|
||||
static_assert(std::is_void<T>::value, "");
|
||||
}
|
||||
|
||||
// Move-only
|
||||
SpyHashStateImpl(const SpyHashStateImpl&) = delete;
|
||||
SpyHashStateImpl& operator=(const SpyHashStateImpl&) = delete;
|
||||
|
||||
SpyHashStateImpl(SpyHashStateImpl&& other) noexcept {
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
SpyHashStateImpl& operator=(SpyHashStateImpl&& other) noexcept {
|
||||
hash_representation_ = std::move(other.hash_representation_);
|
||||
error_ = other.error_;
|
||||
moved_from_ = other.moved_from_;
|
||||
other.moved_from_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
SpyHashStateImpl(SpyHashStateImpl<U>&& other) { // NOLINT
|
||||
hash_representation_ = std::move(other.hash_representation_);
|
||||
error_ = other.error_;
|
||||
moved_from_ = other.moved_from_;
|
||||
other.moved_from_ = true;
|
||||
}
|
||||
|
||||
template <typename A, typename... Args>
|
||||
static SpyHashStateImpl combine(SpyHashStateImpl s, const A& a,
|
||||
const Args&... args) {
|
||||
// Pass an instance of SpyHashStateImpl<A> when trying to combine `A`. This
|
||||
// allows us to test that the user only uses this instance for combine calls
|
||||
// and does not call AbslHashValue directly.
|
||||
// See AbslHashValue implementation at the bottom.
|
||||
s = SpyHashStateImpl<A>::HashStateBase::combine(std::move(s), a);
|
||||
return SpyHashStateImpl::combine(std::move(s), args...);
|
||||
}
|
||||
static SpyHashStateImpl combine(SpyHashStateImpl s) {
|
||||
if (direct_absl_hash_value_error_) {
|
||||
*s.error_ = "AbslHashValue should not be invoked directly.";
|
||||
} else if (s.moved_from_) {
|
||||
*s.error_ = "Used moved-from instance of the hash state object.";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static void SetDirectAbslHashValueError() {
|
||||
direct_absl_hash_value_error_ = true;
|
||||
}
|
||||
|
||||
// Two SpyHashStateImpl objects are equal if they hold equal hash
|
||||
// representations.
|
||||
friend bool operator==(const SpyHashStateImpl& lhs,
|
||||
const SpyHashStateImpl& rhs) {
|
||||
return lhs.hash_representation_ == rhs.hash_representation_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const SpyHashStateImpl& lhs,
|
||||
const SpyHashStateImpl& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
enum class CompareResult {
|
||||
kEqual,
|
||||
kASuffixB,
|
||||
kBSuffixA,
|
||||
kUnequal,
|
||||
};
|
||||
|
||||
static CompareResult Compare(const SpyHashStateImpl& a,
|
||||
const SpyHashStateImpl& b) {
|
||||
const std::string a_flat = absl::StrJoin(a.hash_representation_, "");
|
||||
const std::string b_flat = absl::StrJoin(b.hash_representation_, "");
|
||||
if (a_flat == b_flat) return CompareResult::kEqual;
|
||||
if (absl::EndsWith(a_flat, b_flat)) return CompareResult::kBSuffixA;
|
||||
if (absl::EndsWith(b_flat, a_flat)) return CompareResult::kASuffixB;
|
||||
return CompareResult::kUnequal;
|
||||
}
|
||||
|
||||
// operator<< prints the hash representation as a hex and ASCII dump, to
|
||||
// facilitate debugging.
|
||||
friend std::ostream& operator<<(std::ostream& out,
|
||||
const SpyHashStateImpl& hash_state) {
|
||||
out << "[\n";
|
||||
for (auto& s : hash_state.hash_representation_) {
|
||||
size_t offset = 0;
|
||||
for (char c : s) {
|
||||
if (offset % 16 == 0) {
|
||||
out << absl::StreamFormat("\n0x%04x: ", offset);
|
||||
}
|
||||
if (offset % 2 == 0) {
|
||||
out << " ";
|
||||
}
|
||||
out << absl::StreamFormat("%02x", c);
|
||||
++offset;
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
return out << "]";
|
||||
}
|
||||
|
||||
// The base case of the combine recursion, which writes raw bytes into the
|
||||
// internal buffer.
|
||||
static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state,
|
||||
const unsigned char* begin,
|
||||
size_t size) {
|
||||
hash_state.hash_representation_.emplace_back(
|
||||
reinterpret_cast<const char*>(begin), size);
|
||||
return hash_state;
|
||||
}
|
||||
|
||||
using SpyHashStateImpl::HashStateBase::combine_contiguous;
|
||||
|
||||
absl::optional<std::string> error() const {
|
||||
if (moved_from_) {
|
||||
return "Returned a moved-from instance of the hash state object.";
|
||||
}
|
||||
return *error_;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U>
|
||||
friend class SpyHashStateImpl;
|
||||
|
||||
// This is true if SpyHashStateImpl<T> has been passed to a call of
|
||||
// AbslHashValue with the wrong type. This detects that the user called
|
||||
// AbslHashValue directly (because the hash state type does not match).
|
||||
static bool direct_absl_hash_value_error_;
|
||||
|
||||
|
||||
std::vector<std::string> hash_representation_;
|
||||
// This is a shared_ptr because we want all instances of the particular
|
||||
// SpyHashState run to share the field. This way we can set the error for
|
||||
// use-after-move and all the copies will see it.
|
||||
std::shared_ptr<absl::optional<std::string>> error_;
|
||||
bool moved_from_ = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool SpyHashStateImpl<T>::direct_absl_hash_value_error_;
|
||||
|
||||
template <bool& B>
|
||||
struct OdrUse {
|
||||
constexpr OdrUse() {}
|
||||
bool& b = B;
|
||||
};
|
||||
|
||||
template <void (*)()>
|
||||
struct RunOnStartup {
|
||||
static bool run;
|
||||
static constexpr OdrUse<run> kOdrUse{};
|
||||
};
|
||||
|
||||
template <void (*f)()>
|
||||
bool RunOnStartup<f>::run = (f(), true);
|
||||
|
||||
template <
|
||||
typename T, typename U,
|
||||
// Only trigger for when (T != U),
|
||||
absl::enable_if_t<!std::is_same<T, U>::value, int> = 0,
|
||||
// This statement works in two ways:
|
||||
// - First, it instantiates RunOnStartup and forces the initialization of
|
||||
// `run`, which set the global variable.
|
||||
// - Second, it triggers a SFINAE error disabling the overload to prevent
|
||||
// compile time errors. If we didn't disable the overload we would get
|
||||
// ambiguous overload errors, which we don't want.
|
||||
int = RunOnStartup<SpyHashStateImpl<T>::SetDirectAbslHashValueError>::run>
|
||||
void AbslHashValue(SpyHashStateImpl<T>, const U&);
|
||||
|
||||
using SpyHashState = SpyHashStateImpl<void>;
|
||||
|
||||
} // namespace hash_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue