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