tvl-depot/absl/hash/internal/spy_hash_state.h
Abseil Team 12bc53e031 Export of internal Abseil changes
--
c99f979ad34f155fbeeea69b88bdc7458d89a21c by Derek Mauro <dmauro@google.com>:

Remove a floating point division by zero test.

This isn't testing behavior related to the library, and MSVC warns
about it in opt mode.

PiperOrigin-RevId: 285220804

--
68b015491f0dbf1ab547994673281abd1f34cd4b by Gennadiy Rozental <rogeeff@google.com>:

This CL introduces following changes to the class FlagImpl:
* We eliminate the CommandLineFlagLocks struct. Instead callback guard and callback function are combined into a single CallbackData struct, while primary data lock is stored separately.
* CallbackData member of class FlagImpl is initially set to be nullptr and is only allocated and initialized when a flag's callback is being set. For most flags we do not pay for the extra space and extra absl::Mutex now.
* Primary data guard is stored in data_guard_ data member. This is a properly aligned character buffer of necessary size. During initialization of the flag we construct absl::Mutex in this space using placement new call.
* We now avoid extra value copy after successful attempt to parse value out of string. Instead we swap flag's current value with tentative value we just produced.

PiperOrigin-RevId: 285132636

--
ed45d118fb818969eb13094cf7827c885dfc562c by Tom Manshreck <shreck@google.com>:

Change null-term* (and nul-term*) to NUL-term* in comments

PiperOrigin-RevId: 285036610

--
729619017944db895ce8d6d29c1995aa2e5628a5 by Derek Mauro <dmauro@google.com>:

Use the Posix implementation of thread identity on MinGW.
Some versions of MinGW suffer from thread_local bugs.

PiperOrigin-RevId: 285022920

--
39a25493503c76885bc3254c28f66a251c5b5bb0 by Greg Falcon <gfalcon@google.com>:

Implementation detail change.

Add further ABSL_NAMESPACE_BEGIN and _END annotation macros to files in Abseil.

PiperOrigin-RevId: 285012012
GitOrigin-RevId: c99f979ad34f155fbeeea69b88bdc7458d89a21c
Change-Id: I4c85d3704e45d11a9ac50d562f39640a6adbedc1
2019-12-12 15:37:13 -05:00

231 lines
7.8 KiB
C++

// 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
//
// https://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 {
ABSL_NAMESPACE_BEGIN
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) {
const size_t large_chunk_stride = PiecewiseChunkSize();
if (size > large_chunk_stride) {
// Combining a large contiguous buffer must have the same effect as
// doing it piecewise by the stride length, followed by the (possibly
// empty) remainder.
while (size >= large_chunk_stride) {
hash_state = SpyHashStateImpl::combine_contiguous(
std::move(hash_state), begin, large_chunk_stride);
begin += large_chunk_stride;
size -= large_chunk_stride;
}
}
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),
typename = absl::enable_if_t<!std::is_same<T, U>::value>,
// 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
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_