/*
 * 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 "partition_alloc/buildflags.h"
#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. IgnoringAsciiCaseHashTraits 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 PA_BUILDFLAG(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> {};

// Helper to avoid HashTraits<unsigned> sentinel values (0 and 0xFFFFFFFF)
// by turning them into 1.
constexpr unsigned EnsureValidHash(unsigned hash) {
  return (hash == HashTraits<unsigned>::EmptyValue() ||
          hash == HashTraits<unsigned>::DeletedValue())
             ? 1
             : hash;
}

// 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_
