#ifndef V8_OBJECTS_HASH_TABLE_H_
#define V8_OBJECTS_HASH_TABLE_H_
#include "src/base/compiler-specific.h"
#include "src/base/export-template.h"
#include "src/base/macros.h"
#include "src/common/globals.h"
#include "src/execution/isolate-utils.h"
#include "src/objects/fixed-array.h"
#include "src/objects/smi.h"
#include "src/objects/tagged-field.h"
#include "src/roots/roots.h"
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
template <typename KeyT>
class V8_EXPORT_PRIVATE BaseShape {
public:
using Key = KeyT;
static Tagged<Object> Unwrap(Tagged<Object> key) { return key; }
};
class V8_EXPORT_PRIVATE HashTableBase : public NON_EXPORTED_BASE(FixedArray) {
public:
inline uint32_t NumberOfElements() const;
inline uint32_t NumberOfDeletedElements() const;
inline uint32_t Capacity() const;
inline InternalIndex::Range IterateEntries() const;
inline void ElementAdded();
inline void ElementRemoved();
inline void ElementsRemoved(int n);
static constexpr inline uint32_t ComputeCapacity(uint32_t at_least_space_for);
static const int kNumberOfElementsIndex = 0;
static const int kNumberOfDeletedElementsIndex = 1;
static const int kCapacityIndex = 2;
static const int kPrefixStartIndex = 3;
static const uint32_t kMinCapacity = 4;
inline void SetInitialNumberOfElements(int nof);
protected:
inline void SetNumberOfElements(int nof);
inline void SetNumberOfDeletedElements(int nod);
inline static InternalIndex NextProbe(InternalIndex last, uint32_t number,
uint32_t size) {
return InternalIndex((last.as_uint32() + number) & (size - 1));
}
};
template <typename Derived, typename ShapeT>
class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) HashTable
: public HashTableBase {
public:
using TodoShape = ShapeT;
using Key = typename TodoShape::Key;
template <typename IsolateT>
V8_WARN_UNUSED_RESULT static MaybeHandle<Derived> TryNew(
IsolateT* isolate, uint32_t at_least_space_for,
AllocationType allocation = AllocationType::kYoung,
MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY);
template <typename IsolateT>
V8_WARN_UNUSED_RESULT static Handle<Derived> New(
IsolateT* isolate, uint32_t at_least_space_for,
AllocationType allocation = AllocationType::kYoung,
MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY);
static inline DirectHandle<Map> GetMap(RootsTable& roots);
void IteratePrefix(ObjectVisitor* visitor);
void IterateElements(ObjectVisitor* visitor);
inline static InternalIndex FirstProbe(uint32_t hash, uint32_t size) {
if (!TodoShape::kDoHashSpreading || size <= (1u << TodoShape::kHashBits)) {
return InternalIndex(hash & (size - 1));
}
DCHECK_GE(TodoShape::kHashBits, 1);
DCHECK_LE(TodoShape::kHashBits, 32);
uint32_t coefficient = size >> TodoShape::kHashBits;
DCHECK_GE(coefficient, 2);
return InternalIndex((hash * coefficient) & (size - 1));
}
inline InternalIndex FindEntry(PtrComprCageBase cage_base,
ReadOnlyRoots roots, Key key, int32_t hash);
template <typename IsolateT>
inline InternalIndex FindEntry(IsolateT* isolate, Key key);
void Rehash(PtrComprCageBase cage_base);
static inline bool IsKey(ReadOnlyRoots roots, Tagged<Object> k);
inline bool ToKey(ReadOnlyRoots roots, InternalIndex entry,
Tagged<Object>* out_k);
inline bool ToKey(PtrComprCageBase cage_base, InternalIndex entry,
Tagged<Object>* out_k);
inline Tagged<Object> KeyAt(InternalIndex entry);
inline Tagged<Object> KeyAt(PtrComprCageBase cage_base, InternalIndex entry);
inline Tagged<Object> KeyAt(InternalIndex entry, RelaxedLoadTag tag);
inline Tagged<Object> KeyAt(PtrComprCageBase cage_base, InternalIndex entry,
RelaxedLoadTag tag);
inline void SetKeyAt(InternalIndex entry, Tagged<Object> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
static const int kElementsStartIndex =
kPrefixStartIndex + TodoShape::kPrefixSize;
static const int kEntrySize = TodoShape::kEntrySize;
static_assert(kEntrySize > 0);
static const int kEntryKeyIndex = 0;
static const int kElementsStartOffset =
OFFSET_OF_DATA_START(HashTableBase) + kElementsStartIndex * kTaggedSize;
static const uint32_t kMaxCapacity = base::bits::RoundDownToPowerOfTwo32(
(FixedArray::kMaxLength - kElementsStartIndex) / kEntrySize);
static const int kMinShrinkCapacity = 16;
static const int kMinCapacityForPretenure = 256;
static const int kMaxRegularCapacity = kMaxRegularHeapObjectSize / 32;
static constexpr inline int EntryToIndex(InternalIndex entry) {
return (entry.as_int() * kEntrySize) + kElementsStartIndex;
}
static constexpr inline InternalIndex IndexToEntry(int index) {
return InternalIndex((index - kElementsStartIndex) / kEntrySize);
}
static constexpr inline int SlotToIndex(Address object, Address slot) {
return static_cast<int>((slot - object - sizeof(HashTableBase)) /
kTaggedSize);
}
template <typename IsolateT, template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
V8_WARN_UNUSED_RESULT static HandleType<Derived> EnsureCapacity(
IsolateT* isolate, HandleType<Derived> table, int n = 1,
AllocationType allocation = AllocationType::kYoung);
bool HasSufficientCapacityToAdd(int number_of_additional_elements);
static bool HasSufficientCapacityToAdd(int capacity, int number_of_elements,
int number_of_deleted_elements,
int number_of_additional_elements);
protected:
friend class ObjectHashTable;
template <typename IsolateT>
V8_WARN_UNUSED_RESULT static Handle<Derived> NewInternal(
IsolateT* isolate, uint32_t capacity, AllocationType allocation);
InternalIndex FindInsertionEntry(PtrComprCageBase cage_base,
ReadOnlyRoots roots, uint32_t hash);
template <typename IsolateT>
InternalIndex FindInsertionEntry(IsolateT* isolate, uint32_t hash);
static uint32_t ComputeCapacityWithShrink(uint32_t current_capacity,
uint32_t at_least_room_for);
template <template <typename> typename HandleType>
requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
V8_WARN_UNUSED_RESULT static HandleType<Derived> Shrink(
Isolate* isolate, HandleType<Derived> table, int additionalCapacity = 0);
void Rehash(PtrComprCageBase cage_base, Tagged<Derived> new_table);
inline void set_key(int index, Tagged<Object> value);
inline void set_key(int index, Tagged<Object> value, WriteBarrierMode mode);
private:
static_assert(EntryToIndex(InternalIndex(kMaxRegularCapacity)) <
FixedArray::kMaxRegularLength);
static_assert(v8::base::bits::IsPowerOfTwo(kMaxRegularCapacity));
static const int kMaxRegularEntry = kMaxRegularCapacity / kEntrySize;
static const int kMaxRegularIndex =
EntryToIndex(InternalIndex(kMaxRegularEntry));
static_assert(OffsetOfElementAt(kMaxRegularIndex) <
kMaxRegularHeapObjectSize);
inline void SetCapacity(uint32_t capacity);
InternalIndex EntryForProbe(ReadOnlyRoots roots, Tagged<Object> k, int probe,
InternalIndex expected);
void Swap(InternalIndex entry1, InternalIndex entry2, WriteBarrierMode mode);
};
#define EXTERN_DECLARE_HASH_TABLE(DERIVED, SHAPE) \
extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
HashTable<class DERIVED, SHAPE>; \
\
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
MaybeHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::TryNew(Isolate*, uint32_t, AllocationType, \
MinimumCapacity); \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
MaybeHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::TryNew(LocalIsolate*, uint32_t, \
AllocationType, MinimumCapacity); \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::New(Isolate*, uint32_t, AllocationType, \
MinimumCapacity); \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::New(LocalIsolate*, uint32_t, AllocationType, \
MinimumCapacity); \
\
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity(Isolate*, Handle<DERIVED>, int, \
AllocationType); \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity(LocalIsolate*, Handle<DERIVED>, \
int, AllocationType); \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
DirectHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity( \
Isolate*, DirectHandle<DERIVED>, int, AllocationType); \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
DirectHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::EnsureCapacity( \
LocalIsolate*, DirectHandle<DERIVED>, int, AllocationType); \
\
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) Handle<DERIVED> \
HashTable<DERIVED, SHAPE>::Shrink(Isolate*, Handle<DERIVED>, int); \
extern template EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
DirectHandle<DERIVED> \
HashTable<DERIVED, SHAPE>::Shrink(Isolate*, DirectHandle<DERIVED>, int);
class HashTableKey {
public:
explicit HashTableKey(uint32_t hash) : hash_(hash) {}
virtual bool IsMatch(Tagged<Object> other) = 0;
virtual ~HashTableKey() = default;
uint32_t Hash() const { return hash_; }
protected:
void set_hash(uint32_t hash) {
DCHECK_EQ(0, hash_);
hash_ = hash;
}
private:
uint32_t hash_ = 0;
};
class ObjectHashTableShapeBase : public BaseShape<DirectHandle<Object>> {
public:
static inline bool IsMatch(DirectHandle<Object> key, Tagged<Object> other);
static inline uint32_t Hash(ReadOnlyRoots roots, DirectHandle<Object> key);
static inline uint32_t HashForObject(ReadOnlyRoots roots,
Tagged<Object> object);
static inline DirectHandle<Object> AsHandle(DirectHandle<Object> key);
static const int kPrefixSize = 0;
static const int kEntryValueIndex = 1;
static const int kEntrySize = 2;
static const bool kMatchNeedsHoleCheck = false;
};
class ObjectHashTableShape : public ObjectHashTableShapeBase {
public:
static const bool kDoHashSpreading = false;
static const uint32_t kHashBits = 0;
};
class EphemeronHashTableShape : public ObjectHashTableShapeBase {
public:
static const bool kDoHashSpreading = true;
static const uint32_t kHashBits;
};
template <typename Derived, typename Shape>
class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) ObjectHashTableBase
: public HashTable<Derived, Shape> {
public:
Tagged<Object> Lookup(DirectHandle<Object> key);
Tagged<Object> Lookup(DirectHandle<Object> key, int32_t hash);
Tagged<Object> Lookup(PtrComprCageBase cage_base, DirectHandle<Object> key,
int32_t hash);
Tagged<Object> ValueAt(InternalIndex entry);
static void FillEntriesWithHoles(DirectHandle<Derived>);
static Handle<Derived> Put(Isolate* isolate, Handle<Derived> table,
DirectHandle<Object> key,
DirectHandle<Object> value);
static Handle<Derived> Put(Isolate* isolate, Handle<Derived> table,
DirectHandle<Object> key,
DirectHandle<Object> value, int32_t hash);
static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table,
DirectHandle<Object> key, bool* was_present);
static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table,
DirectHandle<Object> key, bool* was_present,
int32_t hash);
static inline int EntryToValueIndex(InternalIndex entry) {
return HashTable<Derived, Shape>::EntryToIndex(entry) +
Shape::kEntryValueIndex;
}
protected:
void AddEntry(InternalIndex entry, Tagged<Object> key, Tagged<Object> value);
void RemoveEntry(InternalIndex entry);
};
#define EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(DERIVED, SHAPE) \
EXTERN_DECLARE_HASH_TABLE(DERIVED, SHAPE) \
extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) \
ObjectHashTableBase<class DERIVED, SHAPE>;
EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(ObjectHashTable, ObjectHashTableShape)
class V8_EXPORT_PRIVATE ObjectHashTable
: public ObjectHashTableBase<ObjectHashTable, ObjectHashTableShape> {
public:
DECL_PRINTER(ObjectHashTable)
};
EXTERN_DECLARE_OBJECT_BASE_HASH_TABLE(EphemeronHashTable,
EphemeronHashTableShape)
class V8_EXPORT_PRIVATE EphemeronHashTable
: public ObjectHashTableBase<EphemeronHashTable, EphemeronHashTableShape> {
public:
static inline Handle<Map> GetMap(RootsTable& roots);
DECL_PRINTER(EphemeronHashTable)
class BodyDescriptor;
protected:
friend class MarkCompactCollector;
friend class MinorMarkSweepCollector;
friend class ScavengerEphemeronProcessor;
friend class HashTable<EphemeronHashTable, EphemeronHashTableShape>;
friend class ObjectHashTableBase<EphemeronHashTable, EphemeronHashTableShape>;
inline void set_key(int index, Tagged<Object> value);
inline void set_key(int index, Tagged<Object> value, WriteBarrierMode mode);
};
template <int N>
class ObjectMultiHashTableShape : public ObjectHashTableShape {
public:
static const int kEntrySize = 1 + N;
};
template <typename Derived, int N>
class ObjectMultiHashTableBase
: public HashTable<Derived, ObjectMultiHashTableShape<N>> {
public:
static_assert(N > 1, "use ObjectHashTable instead if N = 1");
std::array<Tagged<Object>, N> Lookup(DirectHandle<Object> key);
std::array<Tagged<Object>, N> Lookup(PtrComprCageBase cage_base,
DirectHandle<Object> key);
static Handle<Derived> Put(Isolate* isolate, Handle<Derived> table,
DirectHandle<Object> key,
const std::array<DirectHandle<Object>, N>& values);
private:
void SetEntryValues(InternalIndex entry,
const std::array<DirectHandle<Object>, N>& values);
static constexpr inline int EntryToValueIndexStart(InternalIndex entry) {
return HashTable<Derived, ObjectMultiHashTableShape<N>>::EntryToIndex(
entry) +
ObjectMultiHashTableShape<N>::kEntryValueIndex;
}
};
class ObjectTwoHashTable
: public ObjectMultiHashTableBase<ObjectTwoHashTable, 2> {
};
class ObjectHashSetShape : public ObjectHashTableShape {
public:
static const int kPrefixSize = 0;
static const int kEntrySize = 1;
};
EXTERN_DECLARE_HASH_TABLE(ObjectHashSet, ObjectHashSetShape)
class V8_EXPORT_PRIVATE ObjectHashSet
: public HashTable<ObjectHashSet, ObjectHashSetShape> {
public:
static Handle<ObjectHashSet> Add(Isolate* isolate, Handle<ObjectHashSet> set,
DirectHandle<Object> key);
inline bool Has(Isolate* isolate, DirectHandle<Object> key, int32_t hash);
inline bool Has(Isolate* isolate, DirectHandle<Object> key);
};
class NameToIndexShape : public BaseShape<Tagged<Name>> {
public:
static inline bool IsMatch(Tagged<Name> key, Tagged<Object> other);
static inline uint32_t Hash(ReadOnlyRoots roots, Tagged<Name> key);
static inline uint32_t HashForObject(ReadOnlyRoots roots,
Tagged<Object> object);
static const int kPrefixSize = 0;
static const int kEntryValueIndex = 1;
static const int kEntrySize = 2;
static const bool kMatchNeedsHoleCheck = false;
static const bool kDoHashSpreading = false;
static const uint32_t kHashBits = 0;
};
class V8_EXPORT_PRIVATE NameToIndexHashTable
: public HashTable<NameToIndexHashTable, NameToIndexShape> {
public:
static const int kEntryValueIndex = NameToIndexShape::kEntryValueIndex;
inline static DirectHandle<Map> GetMap(RootsTable& roots);
int Lookup(Tagged<Name> key);
Tagged<Object> ValueAt(InternalIndex entry);
int IndexAt(InternalIndex entry);
template <typename IsolateT>
static Handle<NameToIndexHashTable> Add(IsolateT* isolate,
Handle<NameToIndexHashTable> table,
DirectHandle<Name> key,
int32_t value);
using HashTable<NameToIndexHashTable, NameToIndexShape>::FindInsertionEntry;
DECL_PRINTER(NameToIndexHashTable)
private:
static inline int EntryToValueIndex(InternalIndex entry) {
return EntryToIndex(entry) + NameToIndexShape::kEntryValueIndex;
}
};
class RegisteredSymbolTableShape : public BaseShape<DirectHandle<String>> {
public:
static inline bool IsMatch(DirectHandle<String> key, Tagged<Object> other);
static inline uint32_t Hash(ReadOnlyRoots roots, DirectHandle<String> key);
static inline uint32_t HashForObject(ReadOnlyRoots roots,
Tagged<Object> object);
static const int kPrefixSize = 0;
static const int kEntryValueIndex = 1;
static const int kEntrySize = 2;
static const bool kMatchNeedsHoleCheck = false;
static const bool kDoHashSpreading = false;
static const uint32_t kHashBits = 0;
};
class RegisteredSymbolTable
: public HashTable<RegisteredSymbolTable, RegisteredSymbolTableShape> {
public:
Tagged<Object> SlowReverseLookup(Tagged<Object> value);
Tagged<Object> ValueAt(InternalIndex entry);
inline static DirectHandle<Map> GetMap(RootsTable& roots);
static Handle<RegisteredSymbolTable> Add(Isolate* isolate,
Handle<RegisteredSymbolTable> table,
DirectHandle<String> key,
DirectHandle<Symbol>);
DECL_PRINTER(RegisteredSymbolTable)
private:
static inline int EntryToValueIndex(InternalIndex entry) {
return EntryToIndex(entry) + RegisteredSymbolTableShape::kEntryValueIndex;
}
};
}
}
#include "src/objects/object-macros-undef.h"
#endif