blob: 9b665f857b50912f31535c644e10e47ffc5f949b [file] [log] [blame]
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_OFF_HEAP_HASH_TABLE_H_
#define V8_OBJECTS_OFF_HEAP_HASH_TABLE_H_
#include "src/common/globals.h"
#include "src/execution/isolate-utils.h"
#include "src/objects/compressed-slots.h"
#include "src/objects/slots.h"
#include "src/objects/smi.h"
#include "src/objects/visitors.h"
#include "src/roots/roots.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
// A base class for building off-heap hash tables (e.g. the string table) that
// stores tagged values.
//
// It is a variable sized structure, with a "header" followed directly in memory
// by the elements themselves. These are accessed as offsets from the elements_
// field, which itself provides storage for the first element.
//
// The elements themselves are stored as an open-addressed hash table, with
// quadratic probing and Smi 0 and Smi 1 as the empty and deleted sentinels,
// respectively.
//
// It is a CRTP class whose derived class must provide the following
// definitions:
//
// // The number of elements per table entry.
// static constexpr int kEntrySize;
//
// // The factor by which to decide if the table ought to shrink.
// static constexpr int kMaxEmptyFactor;
//
// // The minimum number of elements for new tables.
// static constexpr int kMinCapacity;
//
// // Computes the hash of a key {obj}.
// static uint32_t Hash(Tagged<Object> obj);
//
// // Returns whether the lookup key {key} matches the key element {obj}.
// template <typename IsolateT, typename Key>
// static bool KeyIsMatch(IsolateT* isolate, Key key, Tagged<Object> obj);
//
// // Load the key object at entry {index}, decompressing it if needed.
// Tagged<Object> GetKey(PtrComprCageBase, InternalIndex index);
//
// // Store the key object at the entry {index}.
// void SetKey(InternalIndex index, Tagged<Object> key);
//
// // Store an entire entry at {index}. The arity of this function must be
// // kEntrySize + 1.
// void Set(InternalIndex index, Tagged<Object>...);
//
// // Copy an entry in this table at {from_index} into the entry in {to} at
// // {to_index}, exclusive of the key.
// void CopyEntryExcludingKeyInto(PtrComprCageBase, InternalIndex from_index,
// Derived* to, InternalIndex to_index);
//
template <typename Derived>
class OffHeapHashTableBase {
public:
static constexpr Tagged<Smi> empty_element() { return Smi::FromInt(0); }
static constexpr Tagged<Smi> deleted_element() { return Smi::FromInt(1); }
static bool IsKey(Tagged<Object> k) {
return k != empty_element() && k != deleted_element();
}
int capacity() const { return capacity_; }
int number_of_elements() const { return number_of_elements_; }
int number_of_deleted_elements() const { return number_of_deleted_elements_; }
OffHeapObjectSlot slot(InternalIndex index, int offset = 0) const {
DCHECK_LT(offset, Derived::kEntrySize);
return OffHeapObjectSlot(
&elements_[index.as_uint32() * Derived::kEntrySize + offset]);
}
template <typename... Args>
void AddAt(PtrComprCageBase cage_base, InternalIndex entry, Args&&... args) {
Derived* derived_this = static_cast<Derived*>(this);
DCHECK_EQ(derived_this->GetKey(cage_base, entry), empty_element());
DCHECK_LT(number_of_elements_ + 1, capacity());
DCHECK(HasSufficientCapacityToAdd(1));
derived_this->Set(entry, std::forward<Args>(args)...);
number_of_elements_++;
}
template <typename... Args>
void OverwriteDeletedAt(PtrComprCageBase cage_base, InternalIndex entry,
Args&&... args) {
Derived* derived_this = static_cast<Derived*>(this);
DCHECK_EQ(derived_this->GetKey(cage_base, entry), deleted_element());
DCHECK_LT(number_of_elements_ + 1, capacity());
DCHECK(HasSufficientCapacityToAdd(capacity(), number_of_elements(),
number_of_deleted_elements() - 1, 1));
derived_this->Set(entry, std::forward<Args>(args)...);
number_of_elements_++;
number_of_deleted_elements_--;
}
void ElementsRemoved(int count) {
DCHECK_LE(count, number_of_elements_);
number_of_elements_ -= count;
number_of_deleted_elements_ += count;
}
size_t GetSizeExcludingHeader() const {
return GetSizeExcludingHeader(capacity_);
}
template <typename IsolateT, typename FindKey>
inline InternalIndex FindEntry(IsolateT* isolate, FindKey key,
uint32_t hash) const;
inline InternalIndex FindInsertionEntry(PtrComprCageBase cage_base,
uint32_t hash) const;
template <typename IsolateT, typename FindKey>
inline InternalIndex FindEntryOrInsertionEntry(IsolateT* isolate, FindKey key,
uint32_t hash) const;
inline bool ShouldResizeToAdd(int number_of_additional_elements,
int* new_capacity);
inline void RehashInto(PtrComprCageBase cage_base, Derived* new_table);
inline void IterateElements(Root root, RootVisitor* visitor);
protected:
explicit OffHeapHashTableBase(int capacity);
// Returns probe entry.
static inline InternalIndex FirstProbe(uint32_t hash, uint32_t size) {
return InternalIndex(hash & (size - 1));
}
static inline InternalIndex NextProbe(InternalIndex last, uint32_t number,
uint32_t size) {
return InternalIndex((last.as_uint32() + number) & (size - 1));
}
bool HasSufficientCapacityToAdd(int number_of_additional_elements) const {
return HasSufficientCapacityToAdd(capacity(), number_of_elements(),
number_of_deleted_elements(),
number_of_additional_elements);
}
static inline bool HasSufficientCapacityToAdd(
int capacity, int number_of_elements, int number_of_deleted_elements,
int number_of_additional_elements);
static inline int ComputeCapacity(int at_least_space_for);
static inline int ComputeCapacityWithShrink(int current_capacity,
int at_least_space_for);
static inline size_t GetSizeExcludingHeader(int capacity) {
// Subtract sizeof(Tagged_t) from the result, as the member elements_
// already supplies the storage for the first element.
return (capacity * sizeof(Tagged_t) * Derived::kEntrySize) -
sizeof(Tagged_t);
}
// Returns memory to hold a Derived, which may be inline inside Container. The
// offset of the elements_ field relative to Container must be passed for
// static layout checks.
template <typename Container, size_t OffsetOfElementsInContainer>
static inline void* Allocate(int capacity);
static inline void Free(void* container);
int number_of_elements_;
int number_of_deleted_elements_;
const int capacity_;
Tagged_t elements_[1];
};
} // namespace internal
} // namespace v8
#endif // V8_OBJECTS_OFF_HEAP_HASH_TABLE_H_