blob: 9c54faa0de475d12d3f8b220a5aede1599fd1c49 [file] [log] [blame]
/*
* Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights
* reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_
#include <string.h>
#include <concepts>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/type_traits.h"
#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
namespace blink {
// A hash traits type is required for a type when the type is used as the key
// or value of a HashTable-based classes. See documentation in
// GenericHashTraitsBase for the HashTraits API.
//
// A hash traits type can be defined as
// - a specialization of the HashTraits template, which will be automatically
// used, or
// - a standalone hash traits type, which should be passed as the *Traits
// template parameters of HashTable-based classes.
// The former is preferred if the hash traits defines the default hash behavior
// of the type. The latter is suitable when a type has multiple hash behaviors,
// e.g. CaseFoldingHashTraits defines an alternative hash behavior of strings.
//
// This file contains definitions of hash traits for integral types,
// floating-point types, enums, raw and smart pointers, std::pair, etc.
// These types can be used as hash key or value directly.
//
// This file also contains hash traits types that can be used as the base class
// of hash traits of other types.
//
// A simple hash traits type for a key type can be like:
// template <>
// HashTraits<KeyType> : GenericHashTraits<KeyType> {
// static unsigned GetHash(const KeyType& key) { ...; }
// static KeyType EmptyValue() { ...; }
// static KeyType DeletedValue() { ...; }
// };
//
// A hash traits type for a value type can be even simpler. See documentation
// in GenericHashTraitsBase for which functions/flags are for key types only
// (i.e. not needed for a value type).
//
template <typename T>
struct HashTraits;
namespace internal {
template <typename T>
struct GenericHashTraitsBase {
STATIC_ONLY(GenericHashTraitsBase);
using TraitType = T;
// Type for functions that do not take ownership, such as contains.
// If overridden, the type must be assignable to T.
using PeekInType = const T&;
// Types for iterators.
using IteratorGetType = T*;
using IteratorConstGetType = const T*;
using IteratorReferenceType = T&;
using IteratorConstReferenceType = const T&;
// Type for return value of functions that do not transfer ownership, such
// as get.
using PeekOutType = T;
static const T& Peek(const T& value) { return value; }
// Computes the hash code.
// This is for key types only.
static unsigned GetHash(const T&) = delete;
// Whether two values are equal. By default, operator== is used.
// This is for key types only.
static bool Equal(const T& a, const T& b) { return a == b; }
// When this is true, the hash table can optimize lookup operations by
// skipping checks for empty or deleted values. It can be true only if
// Equal(a, b) can reliably return false where
// IsHashTraitsEmptyValue(a) != IsHashTraitsEmptyValue(b) or
// IsHashTraitsDeletedValue(a) != IsHashTraitsDeletedValue(b).
// If this is false, the hash table will never call Equal(a, b) where a or b
// is an empty or a deleted value. When T is a pointer type, Equal(a, b) can
// dereference a and b safely without checking if a or b is nullptr or an
// invalid pointer that represents the deleted value.
// This is for key types only.
static constexpr bool kSafeToCompareToEmptyOrDeleted = true;
// Defines the empty value which is used to fill unused slots in the hash
// table. This function is preferred to IsEmptyValue() when the empty value
// can be represented with a value that can be safely and cheaply
// compared/assigned to another value. By default, the default constructor
// is used.
static T EmptyValue() { return T(); }
// Checks if a given value is an empty value. If this is defined, the hash
// table will call this function (through IsHashTraitsEmptyValue()) to check
// if a slot is empty. Otherwise `v == EmptyValue()` will be used.
static void IsEmptyValue(const T& v) = delete;
// When this is true, the hash table can optimize allocation of empty hash
// slots with zeroed memory without calling EmptyValue().
// This flag can be set to true if any of the following conditions is true:
// 1. EmptyValue() is defined and all bytes of the return value are zero.
// 2. IsEmptyValue() is defined and it returns true for a value containing all
// zero bytes.
// Otherwise this flag must be set to false.
static constexpr bool kEmptyValueIsZero = false;
// Defines the deleted value which is used to fill the slot for a hash entry
// when the entry is deleted from the hash table. A hash traits type must
// define either this function or both IsDeletedValue() and
// ConstructDeletedValue(). This function is preferred to IsDeletedValue()
// and ConstructDeletedValue() when the deleted value can be represented with
// a value that can be safely and cheaply compared/assigned to another value.
// This is for key types only.
// NOTE: The destructor of the returned value *may not* be called, so the
// value should not own any dynamically allocated resources.
static T DeletedValue() = delete;
// Checks if a given value is a deleted value. If this is defined, the hash
// table will call this function (through IsHashTraitsDeletedValue()) to check
// if a slot is deleted. Otherwise `v == DeletedValue()` will be used.
// This is for key types only.
static bool IsDeletedValue(const T& v) = delete;
// Constructs a deleted value in-place in the given memory space.
// When this is called, T's destructor on the slot has been called, so this
// function should not call destructor again (e.g. by assigning a value
// to `slot`), unless T is trivially destructible.
// This must be defined if IsDeletedValue() is defined, and will be called
// through ConstructHashTraitsDeletedValue(). Otherwise
// `slot = DeletedValue()` will be used.
// This is for key types only.
// NOTE: The destructor of the constructed value *will not* be called, so the
// value should not own any dynamically allocated resources.
static void ConstructDeletedValue(T& slot) = delete;
// The starting table size. Can be overridden when we know beforehand that a
// hash table will have at least N entries.
#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
// The allocation pool for nodes is one big chunk that ASAN has no insight
// into, so it can cloak errors. Make it as small as possible to force nodes
// to be allocated individually where ASAN can see them.
static constexpr wtf_size_t kMinimumTableSize = 1;
#else
static constexpr wtf_size_t kMinimumTableSize = 8;
#endif
// The NeedsToForbidGCOnMove flag is used to make the hash table move
// operations safe when GC is enabled: if a move constructor invokes
// an allocation triggering the GC then it should be invoked within GC
// forbidden scope.
template <typename U = void>
struct NeedsToForbidGCOnMove {
// TODO(yutak): Consider using of std:::is_trivially_move_constructible
// when it is accessible.
static constexpr bool value =
!std::is_trivial_v<T> || !std::is_standard_layout_v<T>;
};
// The kCanTraceConcurrently value is used by Oilpan concurrent marking. Only
// type for which HashTraits<T>::kCanTraceConcurrently is true can be traced
// on a concurrent thread.
static constexpr bool kCanTraceConcurrently = false;
// Used by Oilpan compaction. Only types that return true here will be
// compacted.
static constexpr bool kSupportsCompaction = false;
};
template <typename T, auto empty_value, auto deleted_value>
struct IntOrEnumHashTraits : internal::GenericHashTraitsBase<T> {
static_assert(std::is_integral_v<T> || std::is_enum_v<T>);
static unsigned GetHash(T key) { return blink::HashInt(key); }
static constexpr bool kEmptyValueIsZero =
static_cast<int64_t>(empty_value) == 0;
static constexpr T EmptyValue() { return static_cast<T>(empty_value); }
static constexpr T DeletedValue() { return static_cast<T>(deleted_value); }
static constexpr bool kSupportsCompaction = true;
};
} // namespace internal
// Default integer traits disallow both 0 and -1 as keys (max value instead of
// -1 for unsigned).
template <typename T, T empty_value = 0, T deleted_value = static_cast<T>(-1)>
struct IntHashTraits
: internal::IntOrEnumHashTraits<T, empty_value, deleted_value> {
static_assert(std::is_integral_v<T>);
};
// Default traits for an enum type. 0 is very popular, and -1 is also popular.
// So we use -128 and -127.
template <typename T>
struct EnumHashTraits : internal::IntOrEnumHashTraits<T, -128, -127> {
static_assert(std::is_enum_v<T>);
};
template <typename T>
struct GenericHashTraits : internal::GenericHashTraitsBase<T> {
static_assert(!std::is_integral_v<T>);
static_assert(!std::is_enum_v<T>);
static_assert(!std::is_floating_point_v<T>);
};
template <typename T>
requires std::integral<T>
struct GenericHashTraits<T> : IntHashTraits<T> {};
template <typename T>
requires std::is_enum_v<T>
struct GenericHashTraits<T> : EnumHashTraits<T> {};
template <typename T>
requires std::floating_point<T>
struct GenericHashTraits<T> : internal::GenericHashTraitsBase<T> {
static unsigned GetHash(T key) { return HashFloat(key); }
static bool Equal(T a, T b) { return FloatEqualForHash(a, b); }
static constexpr T EmptyValue() { return std::numeric_limits<T>::infinity(); }
static constexpr T DeletedValue() {
return -std::numeric_limits<T>::infinity();
}
static constexpr bool kSupportsCompaction = true;
};
// Default integral traits disallow both 0 and max as keys -- use these traits
// to allow zero and disallow max - 1.
template <typename T>
struct IntWithZeroKeyHashTraits
: IntHashTraits<T,
std::numeric_limits<T>::max(),
std::numeric_limits<T>::max() - 1> {};
// This hash traits can be used in cases where the key is already a good hash.
struct AlreadyHashedTraits : GenericHashTraits<unsigned> {
static unsigned GetHash(unsigned key) { return key; }
};
struct AlreadyHashedWithZeroKeyTraits : IntWithZeroKeyHashTraits<unsigned> {
static unsigned GetHash(unsigned key) { return key; }
};
template <typename P>
struct GenericHashTraits<P*> : internal::GenericHashTraitsBase<P*> {
static unsigned GetHash(P* key) { return HashPointer(key); }
static constexpr bool kEmptyValueIsZero = true;
static constexpr P* DeletedValue() { return reinterpret_cast<P*>(-1); }
};
template <typename P>
struct GenericHashTraits<scoped_refptr<P>>
: internal::GenericHashTraitsBase<scoped_refptr<P>> {
static_assert(sizeof(void*) == sizeof(scoped_refptr<P>),
"Unexpected RefPtr size."
" RefPtr needs to be single pointer to support deleted value.");
static unsigned GetHash(P* key) { return HashPointer(key); }
static unsigned GetHash(const scoped_refptr<P>& key) {
return GetHash(key.get());
}
static bool Equal(const scoped_refptr<P>& a, const scoped_refptr<P>& b) {
return a == b;
}
static bool Equal(P* a, const scoped_refptr<P>& b) { return a == b; }
static bool Equal(const scoped_refptr<P>& a, P* b) { return a == b; }
class RefPtrValuePeeker {
DISALLOW_NEW();
public:
ALWAYS_INLINE RefPtrValuePeeker(P* p) : ptr_(p) {}
template <typename U>
RefPtrValuePeeker(const scoped_refptr<U>& p) : ptr_(p.get()) {}
ALWAYS_INLINE operator P*() const { return ptr_; }
private:
P* ptr_;
};
static constexpr bool kEmptyValueIsZero = true;
static bool IsEmptyValue(const scoped_refptr<P>& value) { return !value; }
static bool IsDeletedValue(const scoped_refptr<P>& value) {
return *reinterpret_cast<void* const*>(&value) ==
reinterpret_cast<const void*>(-1);
}
static void ConstructDeletedValue(scoped_refptr<P>& slot) {
*reinterpret_cast<void**>(&slot) = reinterpret_cast<void*>(-1);
}
typedef RefPtrValuePeeker PeekInType;
typedef scoped_refptr<P>* IteratorGetType;
typedef const scoped_refptr<P>* IteratorConstGetType;
typedef scoped_refptr<P>& IteratorReferenceType;
typedef const scoped_refptr<P>& IteratorConstReferenceType;
typedef P* PeekOutType;
static PeekOutType Peek(const scoped_refptr<P>& value) { return value.get(); }
template <typename = void>
struct NeedsToForbidGCOnMove {
static constexpr bool value = false;
};
};
template <typename T>
struct GenericHashTraits<std::unique_ptr<T>>
: internal::GenericHashTraitsBase<std::unique_ptr<T>> {
static unsigned GetHash(T* key) { return HashPointer(key); }
static unsigned GetHash(const std::unique_ptr<T>& key) {
return GetHash(key.get());
}
static bool Equal(const std::unique_ptr<T>& a, const std::unique_ptr<T>& b) {
return a == b;
}
static bool Equal(const std::unique_ptr<T>& a, const T* b) {
return a.get() == b;
}
static bool Equal(const T* a, const std::unique_ptr<T>& b) {
return a == b.get();
}
static constexpr bool kEmptyValueIsZero = true;
static bool IsEmptyValue(const std::unique_ptr<T>& value) { return !value; }
using PeekInType = T*;
using PeekOutType = T*;
static PeekOutType Peek(const std::unique_ptr<T>& value) {
return value.get();
}
static void ConstructDeletedValue(std::unique_ptr<T>& slot) {
// Dirty trick: implant an invalid pointer to unique_ptr. Destructor isn't
// called for deleted buckets, so this is okay.
new (base::NotNullTag::kNotNull, &slot)
std::unique_ptr<T>(reinterpret_cast<T*>(1u));
}
static bool IsDeletedValue(const std::unique_ptr<T>& value) {
return value.get() == reinterpret_cast<T*>(1u);
}
template <typename = void>
struct NeedsToForbidGCOnMove {
static constexpr bool value = false;
};
};
// HashTraits<T> is defined as GenericHashTraits<T> by default.
// The separation of HashTraits<T> and GenericHashTraits<T> is to allow
// a specialized HashTraits<T> to inherit GenericHashTraits<T>.
template <typename T>
struct HashTraits : GenericHashTraits<T> {};
// This hash traits type requires the following methods in class T, unless
// the corresponding hash traits method is overridden:
// // Computes the hash code, for GetHash().
// unsigned GetHash() const;
// // Creates the deleted value, for ConstructDeletedValue().
// T(HashTableDeletedValueType);
// // Checks if `this` is a deleted value, for IsDeletedValue().
// bool IsHashTableDeletedValue() const;
// Also requires T() and operator== if EmptyValue() and Equal() are not
// overridden, respectively, which is the same as GenericHashTraits<T>.
template <typename T>
struct SimpleClassHashTraits : GenericHashTraits<T> {
static_assert(std::is_class_v<T>);
static unsigned GetHash(const T& key) { return key.GetHash(); }
static constexpr bool kEmptyValueIsZero = true;
template <typename U = void>
struct NeedsToForbidGCOnMove {
static constexpr bool value = false;
};
static void ConstructDeletedValue(T& slot) {
new (base::NotNullTag::kNotNull, &slot) T(kHashTableDeletedValue);
}
static bool IsDeletedValue(const T& value) {
return value.IsHashTableDeletedValue();
}
};
// Defined in string_hash.h.
template <>
struct HashTraits<String>;
namespace internal {
template <typename Traits>
struct HashTraitsEmptyValueChecker {
static bool IsEmptyValue(const typename Traits::TraitType& value) {
return value == Traits::EmptyValue();
}
};
template <typename Traits>
requires requires(const typename Traits::TraitType& t) {
{ Traits::IsEmptyValue(t) } -> std::same_as<bool>;
}
struct HashTraitsEmptyValueChecker<Traits> {
static bool IsEmptyValue(const typename Traits::TraitType& value) {
return Traits::IsEmptyValue(value);
}
};
template <typename Traits>
struct HashTraitsDeletedValueHelper {
static bool IsDeletedValue(const typename Traits::TraitType& value) {
return value == Traits::DeletedValue();
}
static void ConstructDeletedValue(typename Traits::TraitType& slot) {
new (base::NotNullTag::kNotNull, &slot)
typename Traits::TraitType(Traits::DeletedValue());
}
};
template <typename Traits>
requires requires(const typename Traits::TraitType& t) {
{ Traits::IsDeletedValue(t) } -> std::same_as<bool>;
}
struct HashTraitsDeletedValueHelper<Traits> {
static bool IsDeletedValue(const typename Traits::TraitType& value) {
return Traits::IsDeletedValue(value);
}
// Traits must also define ConstructDeletedValue() if it defines
// IsDeletedValue().
static void ConstructDeletedValue(typename Traits::TraitType& slot) {
Traits::ConstructDeletedValue(slot);
}
};
} // namespace internal
// This function selects either the EmptyValue() function or the IsEmptyValue()
// function to check for empty values.
template <typename Traits, typename T>
inline bool IsHashTraitsEmptyValue(const T& value) {
return internal::HashTraitsEmptyValueChecker<Traits>::IsEmptyValue(value);
}
// This function selects either the DeletedValue() function or the
// IsDeletedValue() function to check for deleted values.
template <typename Traits, typename T>
inline bool IsHashTraitsDeletedValue(const T& value) {
return internal::HashTraitsDeletedValueHelper<Traits>::IsDeletedValue(value);
}
// This function selects either the DeletedValue() function or the
// ConstructDeletedValue() function to construct a deleted value.
template <typename Traits, typename T>
inline void ConstructHashTraitsDeletedValue(T& slot) {
internal::HashTraitsDeletedValueHelper<Traits>::ConstructDeletedValue(slot);
}
template <typename Traits, typename T>
inline bool IsHashTraitsEmptyOrDeletedValue(const T& value) {
return IsHashTraitsEmptyValue<Traits, T>(value) ||
IsHashTraitsDeletedValue<Traits, T>(value);
}
// A HashTraits type for T to delegate all HashTraits API to a field.
template <typename T,
auto field,
typename FieldTraits = HashTraits<
std::remove_reference_t<decltype(std::declval<T>().*field)>>>
struct OneFieldHashTraits : GenericHashTraits<T> {
using TraitType = T;
static unsigned GetHash(const T& p) { return FieldTraits::GetHash(p.*field); }
static bool Equal(const T& a, const T& b) {
return FieldTraits::Equal(a.*field, b.*field);
}
static constexpr bool kSafeToCompareToEmptyOrDeleted =
FieldTraits::kSafeToCompareToEmptyOrDeleted;
static constexpr bool kEmptyValueIsZero = FieldTraits::kEmptyValueIsZero;
static T EmptyValue() { return T(FieldTraits::EmptyValue()); }
static bool IsEmptyValue(const T& value) {
return IsHashTraitsEmptyValue<FieldTraits>(value.*field);
}
static void ConstructDeletedValue(T& slot) {
ConstructHashTraitsDeletedValue<FieldTraits>(slot.*field);
}
static bool IsDeletedValue(const T& value) {
return IsHashTraitsDeletedValue<FieldTraits>(value.*field);
}
static constexpr wtf_size_t kMinimumTableSize =
FieldTraits::kMinimumTableSize;
template <typename U = void>
struct NeedsToForbidGCOnMove {
static const bool value =
FieldTraits::template NeedsToForbidGCOnMove<>::value;
};
};
// A HashTraits type for T to delegate all HashTraits API to two fields.
template <
typename T,
auto first_field,
auto second_field,
typename FirstTraits = HashTraits<
std::remove_reference_t<decltype(std::declval<T>().*first_field)>>,
typename SecondTraits = HashTraits<
std::remove_reference_t<decltype(std::declval<T>().*second_field)>>>
struct TwoFieldsHashTraits : OneFieldHashTraits<T, first_field, FirstTraits> {
using TraitType = T;
static unsigned GetHash(const T& p) {
return HashInts(FirstTraits::GetHash(p.*first_field),
SecondTraits::GetHash(p.*second_field));
}
static bool Equal(const T& a, const T& b) {
return FirstTraits::Equal(a.*first_field, b.*first_field) &&
SecondTraits::Equal(a.*second_field, b.*second_field);
}
static constexpr bool kSafeToCompareToEmptyOrDeleted =
FirstTraits::kSafeToCompareToEmptyOrDeleted &&
SecondTraits::kSafeToCompareToEmptyOrDeleted;
static constexpr bool kEmptyValueIsZero =
FirstTraits::kEmptyValueIsZero && SecondTraits::kEmptyValueIsZero;
static T EmptyValue() {
return T(FirstTraits::EmptyValue(), SecondTraits::EmptyValue());
}
static bool IsEmptyValue(const T& value) {
return IsHashTraitsEmptyValue<FirstTraits>(value.*first_field) &&
IsHashTraitsEmptyValue<SecondTraits>(value.*second_field);
}
// ConstructDeletedValue(), IsDeletedValue(), kMinimumTableSize delegate to
// the first field, inherited from OneFieldHashTraits.
template <typename U = void>
struct NeedsToForbidGCOnMove {
static const bool value =
FirstTraits::template NeedsToForbidGCOnMove<>::value ||
SecondTraits::template NeedsToForbidGCOnMove<>::value;
};
};
template <typename FirstTraitsArg,
typename SecondTraitsArg,
typename P = std::pair<typename FirstTraitsArg::TraitType,
typename SecondTraitsArg::TraitType>>
struct PairHashTraits : TwoFieldsHashTraits<P,
&P::first,
&P::second,
FirstTraitsArg,
SecondTraitsArg> {
using TraitType = P;
using FirstTraits = FirstTraitsArg;
using SecondTraits = SecondTraitsArg;
};
template <typename First, typename Second>
struct HashTraits<std::pair<First, Second>>
: public PairHashTraits<HashTraits<First>, HashTraits<Second>> {};
// Shortcut of HashTraits<T>::GetHash(), which can deduct T automatically.
template <typename T>
unsigned GetHash(const T& key) {
return HashTraits<T>::GetHash(key);
}
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_