| // Copyright 2017 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_HASH_TABLE_H_ | |
| #define V8_OBJECTS_HASH_TABLE_H_ | |
| #include "src/base/compiler-specific.h" | |
| #include "src/globals.h" | |
| #include "src/objects/fixed-array.h" | |
| // Has to be the last include (doesn't have include guards): | |
| #include "src/objects/object-macros.h" | |
| namespace v8 { | |
| namespace internal { | |
| // HashTable is a subclass of FixedArray that implements a hash table | |
| // that uses open addressing and quadratic probing. | |
| // | |
| // In order for the quadratic probing to work, elements that have not | |
| // yet been used and elements that have been deleted are | |
| // distinguished. Probing continues when deleted elements are | |
| // encountered and stops when unused elements are encountered. | |
| // | |
| // - Elements with key == undefined have not been used yet. | |
| // - Elements with key == the_hole have been deleted. | |
| // | |
| // The hash table class is parameterized with a Shape. | |
| // Shape must be a class with the following interface: | |
| // class ExampleShape { | |
| // public: | |
| // // Tells whether key matches other. | |
| // static bool IsMatch(Key key, Object* other); | |
| // // Returns the hash value for key. | |
| // static uint32_t Hash(Isolate* isolate, Key key); | |
| // // Returns the hash value for object. | |
| // static uint32_t HashForObject(Isolate* isolate, Object* object); | |
| // // Convert key to an object. | |
| // static inline Handle<Object> AsHandle(Isolate* isolate, Key key); | |
| // // The prefix size indicates number of elements in the beginning | |
| // // of the backing storage. | |
| // static const int kPrefixSize = ..; | |
| // // The Element size indicates number of elements per entry. | |
| // static const int kEntrySize = ..; | |
| // // Indicates whether IsMatch can deal with other being the_hole (a | |
| // // deleted entry). | |
| // static const bool kNeedsHoleCheck = ..; | |
| // }; | |
| // The prefix size indicates an amount of memory in the | |
| // beginning of the backing storage that can be used for non-element | |
| // information by subclasses. | |
| template <typename KeyT> | |
| class BaseShape { | |
| public: | |
| typedef KeyT Key; | |
| static inline int GetMapRootIndex(); | |
| static const bool kNeedsHoleCheck = true; | |
| static Object* Unwrap(Object* key) { return key; } | |
| static inline bool IsKey(ReadOnlyRoots roots, Object* key); | |
| static inline bool IsLive(ReadOnlyRoots roots, Object* key); | |
| }; | |
| class V8_EXPORT_PRIVATE HashTableBase : public NON_EXPORTED_BASE(FixedArray) { | |
| public: | |
| // Returns the number of elements in the hash table. | |
| inline int NumberOfElements() const; | |
| // Returns the number of deleted elements in the hash table. | |
| inline int NumberOfDeletedElements() const; | |
| // Returns the capacity of the hash table. | |
| inline int Capacity() const; | |
| // ElementAdded should be called whenever an element is added to a | |
| // hash table. | |
| inline void ElementAdded(); | |
| // ElementRemoved should be called whenever an element is removed from | |
| // a hash table. | |
| inline void ElementRemoved(); | |
| inline void ElementsRemoved(int n); | |
| // Computes the required capacity for a table holding the given | |
| // number of elements. May be more than HashTable::kMaxCapacity. | |
| static inline int ComputeCapacity(int at_least_space_for); | |
| // Compute the probe offset (quadratic probing). | |
| V8_INLINE static uint32_t GetProbeOffset(uint32_t n) { | |
| return (n + n * n) >> 1; | |
| } | |
| static const int kNumberOfElementsIndex = 0; | |
| static const int kNumberOfDeletedElementsIndex = 1; | |
| static const int kCapacityIndex = 2; | |
| static const int kPrefixStartIndex = 3; | |
| // Constant used for denoting a absent entry. | |
| static const int kNotFound = -1; | |
| // Minimum capacity for newly created hash tables. | |
| static const int kMinCapacity = 4; | |
| protected: | |
| // Update the number of elements in the hash table. | |
| inline void SetNumberOfElements(int nof); | |
| // Update the number of deleted elements in the hash table. | |
| inline void SetNumberOfDeletedElements(int nod); | |
| // Returns probe entry. | |
| static uint32_t GetProbe(uint32_t hash, uint32_t number, uint32_t size) { | |
| DCHECK(base::bits::IsPowerOfTwo(size)); | |
| return (hash + GetProbeOffset(number)) & (size - 1); | |
| } | |
| inline static uint32_t FirstProbe(uint32_t hash, uint32_t size) { | |
| return hash & (size - 1); | |
| } | |
| inline static uint32_t NextProbe(uint32_t last, uint32_t number, | |
| uint32_t size) { | |
| return (last + number) & (size - 1); | |
| } | |
| }; | |
| template <typename Derived, typename Shape> | |
| class HashTable : public HashTableBase { | |
| public: | |
| typedef Shape ShapeT; | |
| typedef typename Shape::Key Key; | |
| // Returns a new HashTable object. | |
| V8_WARN_UNUSED_RESULT static Handle<Derived> New( | |
| Isolate* isolate, int at_least_space_for, | |
| PretenureFlag pretenure = NOT_TENURED, | |
| MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY); | |
| DECL_CAST(HashTable) | |
| // Garbage collection support. | |
| void IteratePrefix(ObjectVisitor* visitor); | |
| void IterateElements(ObjectVisitor* visitor); | |
| // Find entry for key otherwise return kNotFound. | |
| inline int FindEntry(ReadOnlyRoots roots, Key key, int32_t hash); | |
| int FindEntry(Isolate* isolate, Key key); | |
| // Rehashes the table in-place. | |
| void Rehash(Isolate* isolate); | |
| // Tells whether k is a real key. The hole and undefined are not allowed | |
| // as keys and can be used to indicate missing or deleted elements. | |
| static bool IsKey(ReadOnlyRoots roots, Object* k); | |
| inline bool ToKey(ReadOnlyRoots roots, int entry, Object** out_k); | |
| // Returns the key at entry. | |
| Object* KeyAt(int entry) { return get(EntryToIndex(entry) + kEntryKeyIndex); } | |
| static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize; | |
| static const int kEntrySize = Shape::kEntrySize; | |
| STATIC_ASSERT(kEntrySize > 0); | |
| static const int kEntryKeyIndex = 0; | |
| static const int kElementsStartOffset = | |
| kHeaderSize + kElementsStartIndex * kPointerSize; | |
| // Maximal capacity of HashTable. Based on maximal length of underlying | |
| // FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex | |
| // cannot overflow. | |
| static const int kMaxCapacity = | |
| (FixedArray::kMaxLength - kElementsStartIndex) / kEntrySize; | |
| // Don't shrink a HashTable below this capacity. | |
| static const int kMinShrinkCapacity = 16; | |
| // Maximum length to create a regular HashTable (aka. non large object). | |
| static const int kMaxRegularCapacity = 16384; | |
| // Returns the index for an entry (of the key) | |
| static constexpr inline int EntryToIndex(int entry) { | |
| return (entry * kEntrySize) + kElementsStartIndex; | |
| } | |
| // Ensure enough space for n additional elements. | |
| V8_WARN_UNUSED_RESULT static Handle<Derived> EnsureCapacity( | |
| Isolate* isolate, Handle<Derived> table, int n, | |
| PretenureFlag pretenure = NOT_TENURED); | |
| // Returns true if this table has sufficient capacity for adding n elements. | |
| bool HasSufficientCapacityToAdd(int number_of_additional_elements); | |
| protected: | |
| friend class ObjectHashTable; | |
| V8_WARN_UNUSED_RESULT static Handle<Derived> NewInternal( | |
| Isolate* isolate, int capacity, PretenureFlag pretenure); | |
| // Find the entry at which to insert element with the given key that | |
| // has the given hash value. | |
| uint32_t FindInsertionEntry(uint32_t hash); | |
| // Attempt to shrink hash table after removal of key. | |
| V8_WARN_UNUSED_RESULT static Handle<Derived> Shrink( | |
| Isolate* isolate, Handle<Derived> table, int additionalCapacity = 0); | |
| private: | |
| // Ensure that kMaxRegularCapacity yields a non-large object dictionary. | |
| STATIC_ASSERT(EntryToIndex(kMaxRegularCapacity) < kMaxRegularLength); | |
| STATIC_ASSERT(v8::base::bits::IsPowerOfTwo(kMaxRegularCapacity)); | |
| static const int kMaxRegularEntry = kMaxRegularCapacity / kEntrySize; | |
| static const int kMaxRegularIndex = EntryToIndex(kMaxRegularEntry); | |
| STATIC_ASSERT(OffsetOfElementAt(kMaxRegularIndex) < | |
| kMaxRegularHeapObjectSize); | |
| // Sets the capacity of the hash table. | |
| void SetCapacity(int capacity) { | |
| // To scale a computed hash code to fit within the hash table, we | |
| // use bit-wise AND with a mask, so the capacity must be positive | |
| // and non-zero. | |
| DCHECK_GT(capacity, 0); | |
| DCHECK_LE(capacity, kMaxCapacity); | |
| set(kCapacityIndex, Smi::FromInt(capacity)); | |
| } | |
| // Returns _expected_ if one of entries given by the first _probe_ probes is | |
| // equal to _expected_. Otherwise, returns the entry given by the probe | |
| // number _probe_. | |
| uint32_t EntryForProbe(Isolate* isolate, Object* k, int probe, | |
| uint32_t expected); | |
| void Swap(uint32_t entry1, uint32_t entry2, WriteBarrierMode mode); | |
| // Rehashes this hash-table into the new table. | |
| void Rehash(Isolate* isolate, Derived* new_table); | |
| }; | |
| // HashTableKey is an abstract superclass for virtual key behavior. | |
| class HashTableKey { | |
| public: | |
| explicit HashTableKey(uint32_t hash) : hash_(hash) {} | |
| // Returns whether the other object matches this key. | |
| virtual bool IsMatch(Object* other) = 0; | |
| // Returns the hash value for this key. | |
| // Required. | |
| virtual ~HashTableKey() {} | |
| 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 ObjectHashTableShape : public BaseShape<Handle<Object>> { | |
| public: | |
| static inline bool IsMatch(Handle<Object> key, Object* other); | |
| static inline uint32_t Hash(Isolate* isolate, Handle<Object> key); | |
| static inline uint32_t HashForObject(Isolate* isolate, Object* object); | |
| static inline Handle<Object> AsHandle(Handle<Object> key); | |
| static const int kPrefixSize = 0; | |
| static const int kEntryValueIndex = 1; | |
| static const int kEntrySize = 2; | |
| static const bool kNeedsHoleCheck = false; | |
| }; | |
| template <typename Derived, typename Shape> | |
| class ObjectHashTableBase : public HashTable<Derived, Shape> { | |
| public: | |
| // Looks up the value associated with the given key. The hole value is | |
| // returned in case the key is not present. | |
| Object* Lookup(Handle<Object> key); | |
| Object* Lookup(Handle<Object> key, int32_t hash); | |
| Object* Lookup(ReadOnlyRoots roots, Handle<Object> key, int32_t hash); | |
| // Returns the value at entry. | |
| Object* ValueAt(int entry); | |
| // Overwrite all keys and values with the hole value. | |
| static void FillEntriesWithHoles(Handle<Derived>); | |
| // Adds (or overwrites) the value associated with the given key. | |
| static Handle<Derived> Put(Handle<Derived> table, Handle<Object> key, | |
| Handle<Object> value); | |
| static Handle<Derived> Put(Isolate* isolate, Handle<Derived> table, | |
| Handle<Object> key, Handle<Object> value, | |
| int32_t hash); | |
| // Returns an ObjectHashTable (possibly |table|) where |key| has been removed. | |
| static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table, | |
| Handle<Object> key, bool* was_present); | |
| static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table, | |
| Handle<Object> key, bool* was_present, | |
| int32_t hash); | |
| // Returns the index to the value of an entry. | |
| static inline int EntryToValueIndex(int entry) { | |
| return HashTable<Derived, Shape>::EntryToIndex(entry) + | |
| Shape::kEntryValueIndex; | |
| } | |
| protected: | |
| void AddEntry(int entry, Object* key, Object* value); | |
| void RemoveEntry(int entry); | |
| }; | |
| // ObjectHashTable maps keys that are arbitrary objects to object values by | |
| // using the identity hash of the key for hashing purposes. | |
| class ObjectHashTable | |
| : public ObjectHashTableBase<ObjectHashTable, ObjectHashTableShape> { | |
| public: | |
| DECL_CAST(ObjectHashTable) | |
| DECL_PRINTER(ObjectHashTable) | |
| }; | |
| class EphemeronHashTableShape : public ObjectHashTableShape { | |
| public: | |
| static inline int GetMapRootIndex(); | |
| }; | |
| // EphemeronHashTable is similar to ObjectHashTable but gets special treatment | |
| // by the GC. The GC treats its entries as ephemerons: both key and value are | |
| // weak references, however if the key is strongly reachable its corresponding | |
| // value is also kept alive. | |
| class EphemeronHashTable | |
| : public ObjectHashTableBase<EphemeronHashTable, EphemeronHashTableShape> { | |
| public: | |
| DECL_CAST(EphemeronHashTable) | |
| DECL_PRINTER(EphemeronHashTable) | |
| protected: | |
| friend class MarkCompactCollector; | |
| }; | |
| class ObjectHashSetShape : public ObjectHashTableShape { | |
| public: | |
| static const int kPrefixSize = 0; | |
| static const int kEntrySize = 1; | |
| }; | |
| class ObjectHashSet : public HashTable<ObjectHashSet, ObjectHashSetShape> { | |
| public: | |
| static Handle<ObjectHashSet> Add(Isolate* isolate, Handle<ObjectHashSet> set, | |
| Handle<Object> key); | |
| inline bool Has(Isolate* isolate, Handle<Object> key, int32_t hash); | |
| inline bool Has(Isolate* isolate, Handle<Object> key); | |
| DECL_CAST(ObjectHashSet) | |
| }; | |
| } // namespace internal | |
| } // namespace v8 | |
| #include "src/objects/object-macros-undef.h" | |
| #endif // V8_OBJECTS_HASH_TABLE_H_ |