• Home
  • 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