• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // Copyright 2017 the V8 project authors. All rights reserved.
2  // Use of this source code is governed by a BSD-style license that can be
3  // found in the LICENSE file.
4  
5  #ifndef V8_OBJECTS_HASH_TABLE_H_
6  #define V8_OBJECTS_HASH_TABLE_H_
7  
8  #include "src/base/compiler-specific.h"
9  #include "src/globals.h"
10  #include "src/objects/fixed-array.h"
11  
12  // Has to be the last include (doesn't have include guards):
13  #include "src/objects/object-macros.h"
14  
15  namespace v8 {
16  namespace internal {
17  
18  // HashTable is a subclass of FixedArray that implements a hash table
19  // that uses open addressing and quadratic probing.
20  //
21  // In order for the quadratic probing to work, elements that have not
22  // yet been used and elements that have been deleted are
23  // distinguished.  Probing continues when deleted elements are
24  // encountered and stops when unused elements are encountered.
25  //
26  // - Elements with key == undefined have not been used yet.
27  // - Elements with key == the_hole have been deleted.
28  //
29  // The hash table class is parameterized with a Shape.
30  // Shape must be a class with the following interface:
31  //   class ExampleShape {
32  //    public:
33  //     // Tells whether key matches other.
34  //     static bool IsMatch(Key key, Object* other);
35  //     // Returns the hash value for key.
36  //     static uint32_t Hash(Isolate* isolate, Key key);
37  //     // Returns the hash value for object.
38  //     static uint32_t HashForObject(Isolate* isolate, Object* object);
39  //     // Convert key to an object.
40  //     static inline Handle<Object> AsHandle(Isolate* isolate, Key key);
41  //     // The prefix size indicates number of elements in the beginning
42  //     // of the backing storage.
43  //     static const int kPrefixSize = ..;
44  //     // The Element size indicates number of elements per entry.
45  //     static const int kEntrySize = ..;
46  //     // Indicates whether IsMatch can deal with other being the_hole (a
47  //     // deleted entry).
48  //     static const bool kNeedsHoleCheck = ..;
49  //   };
50  // The prefix size indicates an amount of memory in the
51  // beginning of the backing storage that can be used for non-element
52  // information by subclasses.
53  
54  template <typename KeyT>
55  class BaseShape {
56   public:
57    typedef KeyT Key;
58    static inline int GetMapRootIndex();
59    static const bool kNeedsHoleCheck = true;
Unwrap(Object * key)60    static Object* Unwrap(Object* key) { return key; }
61    static inline bool IsKey(ReadOnlyRoots roots, Object* key);
62    static inline bool IsLive(ReadOnlyRoots roots, Object* key);
63  };
64  
NON_EXPORTED_BASE(FixedArray)65  class V8_EXPORT_PRIVATE HashTableBase : public NON_EXPORTED_BASE(FixedArray) {
66   public:
67    // Returns the number of elements in the hash table.
68    inline int NumberOfElements() const;
69  
70    // Returns the number of deleted elements in the hash table.
71    inline int NumberOfDeletedElements() const;
72  
73    // Returns the capacity of the hash table.
74    inline int Capacity() const;
75  
76    // ElementAdded should be called whenever an element is added to a
77    // hash table.
78    inline void ElementAdded();
79  
80    // ElementRemoved should be called whenever an element is removed from
81    // a hash table.
82    inline void ElementRemoved();
83    inline void ElementsRemoved(int n);
84  
85    // Computes the required capacity for a table holding the given
86    // number of elements. May be more than HashTable::kMaxCapacity.
87    static inline int ComputeCapacity(int at_least_space_for);
88  
89    // Compute the probe offset (quadratic probing).
90    V8_INLINE static uint32_t GetProbeOffset(uint32_t n) {
91      return (n + n * n) >> 1;
92    }
93  
94    static const int kNumberOfElementsIndex = 0;
95    static const int kNumberOfDeletedElementsIndex = 1;
96    static const int kCapacityIndex = 2;
97    static const int kPrefixStartIndex = 3;
98  
99    // Constant used for denoting a absent entry.
100    static const int kNotFound = -1;
101  
102    // Minimum capacity for newly created hash tables.
103    static const int kMinCapacity = 4;
104  
105   protected:
106    // Update the number of elements in the hash table.
107    inline void SetNumberOfElements(int nof);
108  
109    // Update the number of deleted elements in the hash table.
110    inline void SetNumberOfDeletedElements(int nod);
111  
112    // Returns probe entry.
113    static uint32_t GetProbe(uint32_t hash, uint32_t number, uint32_t size) {
114      DCHECK(base::bits::IsPowerOfTwo(size));
115      return (hash + GetProbeOffset(number)) & (size - 1);
116    }
117  
118    inline static uint32_t FirstProbe(uint32_t hash, uint32_t size) {
119      return hash & (size - 1);
120    }
121  
122    inline static uint32_t NextProbe(uint32_t last, uint32_t number,
123                                     uint32_t size) {
124      return (last + number) & (size - 1);
125    }
126  };
127  
128  template <typename Derived, typename Shape>
129  class HashTable : public HashTableBase {
130   public:
131    typedef Shape ShapeT;
132    typedef typename Shape::Key Key;
133  
134    // Returns a new HashTable object.
135    V8_WARN_UNUSED_RESULT static Handle<Derived> New(
136        Isolate* isolate, int at_least_space_for,
137        PretenureFlag pretenure = NOT_TENURED,
138        MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY);
139  
140    DECL_CAST(HashTable)
141  
142    // Garbage collection support.
143    void IteratePrefix(ObjectVisitor* visitor);
144    void IterateElements(ObjectVisitor* visitor);
145  
146    // Find entry for key otherwise return kNotFound.
147    inline int FindEntry(ReadOnlyRoots roots, Key key, int32_t hash);
148    int FindEntry(Isolate* isolate, Key key);
149  
150    // Rehashes the table in-place.
151    void Rehash(Isolate* isolate);
152  
153    // Tells whether k is a real key.  The hole and undefined are not allowed
154    // as keys and can be used to indicate missing or deleted elements.
155    static bool IsKey(ReadOnlyRoots roots, Object* k);
156  
157    inline bool ToKey(ReadOnlyRoots roots, int entry, Object** out_k);
158  
159    // Returns the key at entry.
KeyAt(int entry)160    Object* KeyAt(int entry) { return get(EntryToIndex(entry) + kEntryKeyIndex); }
161  
162    static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize;
163    static const int kEntrySize = Shape::kEntrySize;
164    STATIC_ASSERT(kEntrySize > 0);
165    static const int kEntryKeyIndex = 0;
166    static const int kElementsStartOffset =
167        kHeaderSize + kElementsStartIndex * kPointerSize;
168    // Maximal capacity of HashTable. Based on maximal length of underlying
169    // FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex
170    // cannot overflow.
171    static const int kMaxCapacity =
172        (FixedArray::kMaxLength - kElementsStartIndex) / kEntrySize;
173  
174    // Don't shrink a HashTable below this capacity.
175    static const int kMinShrinkCapacity = 16;
176  
177    // Maximum length to create a regular HashTable (aka. non large object).
178    static const int kMaxRegularCapacity = 16384;
179  
180    // Returns the index for an entry (of the key)
EntryToIndex(int entry)181    static constexpr inline int EntryToIndex(int entry) {
182      return (entry * kEntrySize) + kElementsStartIndex;
183    }
184  
185    // Ensure enough space for n additional elements.
186    V8_WARN_UNUSED_RESULT static Handle<Derived> EnsureCapacity(
187        Isolate* isolate, Handle<Derived> table, int n,
188        PretenureFlag pretenure = NOT_TENURED);
189  
190    // Returns true if this table has sufficient capacity for adding n elements.
191    bool HasSufficientCapacityToAdd(int number_of_additional_elements);
192  
193   protected:
194    friend class ObjectHashTable;
195  
196    V8_WARN_UNUSED_RESULT static Handle<Derived> NewInternal(
197        Isolate* isolate, int capacity, PretenureFlag pretenure);
198  
199    // Find the entry at which to insert element with the given key that
200    // has the given hash value.
201    uint32_t FindInsertionEntry(uint32_t hash);
202  
203    // Attempt to shrink hash table after removal of key.
204    V8_WARN_UNUSED_RESULT static Handle<Derived> Shrink(
205        Isolate* isolate, Handle<Derived> table, int additionalCapacity = 0);
206  
207   private:
208    // Ensure that kMaxRegularCapacity yields a non-large object dictionary.
209    STATIC_ASSERT(EntryToIndex(kMaxRegularCapacity) < kMaxRegularLength);
210    STATIC_ASSERT(v8::base::bits::IsPowerOfTwo(kMaxRegularCapacity));
211    static const int kMaxRegularEntry = kMaxRegularCapacity / kEntrySize;
212    static const int kMaxRegularIndex = EntryToIndex(kMaxRegularEntry);
213    STATIC_ASSERT(OffsetOfElementAt(kMaxRegularIndex) <
214                  kMaxRegularHeapObjectSize);
215  
216    // Sets the capacity of the hash table.
SetCapacity(int capacity)217    void SetCapacity(int capacity) {
218      // To scale a computed hash code to fit within the hash table, we
219      // use bit-wise AND with a mask, so the capacity must be positive
220      // and non-zero.
221      DCHECK_GT(capacity, 0);
222      DCHECK_LE(capacity, kMaxCapacity);
223      set(kCapacityIndex, Smi::FromInt(capacity));
224    }
225  
226    // Returns _expected_ if one of entries given by the first _probe_ probes is
227    // equal to  _expected_. Otherwise, returns the entry given by the probe
228    // number _probe_.
229    uint32_t EntryForProbe(Isolate* isolate, Object* k, int probe,
230                           uint32_t expected);
231  
232    void Swap(uint32_t entry1, uint32_t entry2, WriteBarrierMode mode);
233  
234    // Rehashes this hash-table into the new table.
235    void Rehash(Isolate* isolate, Derived* new_table);
236  };
237  
238  // HashTableKey is an abstract superclass for virtual key behavior.
239  class HashTableKey {
240   public:
HashTableKey(uint32_t hash)241    explicit HashTableKey(uint32_t hash) : hash_(hash) {}
242  
243    // Returns whether the other object matches this key.
244    virtual bool IsMatch(Object* other) = 0;
245    // Returns the hash value for this key.
246    // Required.
~HashTableKey()247    virtual ~HashTableKey() {}
248  
Hash()249    uint32_t Hash() const { return hash_; }
250  
251   protected:
set_hash(uint32_t hash)252    void set_hash(uint32_t hash) {
253      DCHECK_EQ(0, hash_);
254      hash_ = hash;
255    }
256  
257   private:
258    uint32_t hash_ = 0;
259  };
260  
261  class ObjectHashTableShape : public BaseShape<Handle<Object>> {
262   public:
263    static inline bool IsMatch(Handle<Object> key, Object* other);
264    static inline uint32_t Hash(Isolate* isolate, Handle<Object> key);
265    static inline uint32_t HashForObject(Isolate* isolate, Object* object);
266    static inline Handle<Object> AsHandle(Handle<Object> key);
267    static const int kPrefixSize = 0;
268    static const int kEntryValueIndex = 1;
269    static const int kEntrySize = 2;
270    static const bool kNeedsHoleCheck = false;
271  };
272  
273  template <typename Derived, typename Shape>
274  class ObjectHashTableBase : public HashTable<Derived, Shape> {
275   public:
276    // Looks up the value associated with the given key. The hole value is
277    // returned in case the key is not present.
278    Object* Lookup(Handle<Object> key);
279    Object* Lookup(Handle<Object> key, int32_t hash);
280    Object* Lookup(ReadOnlyRoots roots, Handle<Object> key, int32_t hash);
281  
282    // Returns the value at entry.
283    Object* ValueAt(int entry);
284  
285    // Overwrite all keys and values with the hole value.
286    static void FillEntriesWithHoles(Handle<Derived>);
287  
288    // Adds (or overwrites) the value associated with the given key.
289    static Handle<Derived> Put(Handle<Derived> table, Handle<Object> key,
290                               Handle<Object> value);
291    static Handle<Derived> Put(Isolate* isolate, Handle<Derived> table,
292                               Handle<Object> key, Handle<Object> value,
293                               int32_t hash);
294  
295    // Returns an ObjectHashTable (possibly |table|) where |key| has been removed.
296    static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table,
297                                  Handle<Object> key, bool* was_present);
298    static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table,
299                                  Handle<Object> key, bool* was_present,
300                                  int32_t hash);
301  
302    // Returns the index to the value of an entry.
EntryToValueIndex(int entry)303    static inline int EntryToValueIndex(int entry) {
304      return HashTable<Derived, Shape>::EntryToIndex(entry) +
305             Shape::kEntryValueIndex;
306    }
307  
308   protected:
309    void AddEntry(int entry, Object* key, Object* value);
310    void RemoveEntry(int entry);
311  };
312  
313  // ObjectHashTable maps keys that are arbitrary objects to object values by
314  // using the identity hash of the key for hashing purposes.
315  class ObjectHashTable
316      : public ObjectHashTableBase<ObjectHashTable, ObjectHashTableShape> {
317   public:
318    DECL_CAST(ObjectHashTable)
319    DECL_PRINTER(ObjectHashTable)
320  };
321  
322  class EphemeronHashTableShape : public ObjectHashTableShape {
323   public:
324    static inline int GetMapRootIndex();
325  };
326  
327  // EphemeronHashTable is similar to ObjectHashTable but gets special treatment
328  // by the GC. The GC treats its entries as ephemerons: both key and value are
329  // weak references, however if the key is strongly reachable its corresponding
330  // value is also kept alive.
331  class EphemeronHashTable
332      : public ObjectHashTableBase<EphemeronHashTable, EphemeronHashTableShape> {
333   public:
334    DECL_CAST(EphemeronHashTable)
335    DECL_PRINTER(EphemeronHashTable)
336  
337   protected:
338    friend class MarkCompactCollector;
339  };
340  
341  class ObjectHashSetShape : public ObjectHashTableShape {
342   public:
343    static const int kPrefixSize = 0;
344    static const int kEntrySize = 1;
345  };
346  
347  class ObjectHashSet : public HashTable<ObjectHashSet, ObjectHashSetShape> {
348   public:
349    static Handle<ObjectHashSet> Add(Isolate* isolate, Handle<ObjectHashSet> set,
350                                     Handle<Object> key);
351  
352    inline bool Has(Isolate* isolate, Handle<Object> key, int32_t hash);
353    inline bool Has(Isolate* isolate, Handle<Object> key);
354  
355    DECL_CAST(ObjectHashSet)
356  };
357  
358  }  // namespace internal
359  }  // namespace v8
360  
361  #include "src/objects/object-macros-undef.h"
362  
363  #endif  // V8_OBJECTS_HASH_TABLE_H_
364