1 // Copyright 2018 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_ORDERED_HASH_TABLE_H_
6 #define V8_OBJECTS_ORDERED_HASH_TABLE_H_
7
8 #include "src/base/export-template.h"
9 #include "src/common/globals.h"
10 #include "src/objects/fixed-array.h"
11 #include "src/objects/internal-index.h"
12 #include "src/objects/js-objects.h"
13 #include "src/objects/smi.h"
14 #include "src/roots/roots.h"
15
16 // Has to be the last include (doesn't have include guards):
17 #include "src/objects/object-macros.h"
18
19 namespace v8 {
20 namespace internal {
21
22 // OrderedHashTable is a HashTable with Object keys that preserves
23 // insertion order. There are Map and Set interfaces (OrderedHashMap
24 // and OrderedHashTable, below). It is meant to be used by JSMap/JSSet.
25 //
26 // Only Object keys are supported, with Object::SameValueZero() used as the
27 // equality operator and Object::GetHash() for the hash function.
28 //
29 // Based on the "Deterministic Hash Table" as described by Jason Orendorff at
30 // https://wiki.mozilla.org/User:Jorend/Deterministic_hash_tables
31 // Originally attributed to Tyler Close.
32 //
33 // Memory layout:
34 // [0] : Prefix
35 // [kPrefixSize]: element count
36 // [kPrefixSize + 1]: deleted element count
37 // [kPrefixSize + 2]: bucket count
38 // [kPrefixSize + 3..(kPrefixSize + 3 + NumberOfBuckets() - 1)]: "hash table",
39 // where each item is an offset into the
40 // data table (see below) where the first
41 // item in this bucket is stored.
42 // [kPrefixSize + 3 + NumberOfBuckets()..length]: "data table", an
43 // array of length Capacity() * kEntrySize,
44 // where the first entrysize items are
45 // handled by the derived class and the
46 // item at kChainOffset is another entry
47 // into the data table indicating the next
48 // entry in this hash bucket.
49 //
50 // When we transition the table to a new version we obsolete it and reuse parts
51 // of the memory to store information how to transition an iterator to the new
52 // table:
53 //
54 // Memory layout for obsolete table:
55 // [0] : Prefix
56 // [kPrefixSize + 0]: Next newer table
57 // [kPrefixSize + 1]: deleted element count or kClearedTableSentinel if
58 // the table was cleared
59 // [kPrefixSize + 2]: bucket count
60 // [kPrefixSize + 3..(kPrefixSize + 3 + NumberOfDeletedElements() - 1)]:
61 // The indexes of the removed holes. This part is only
62 // usable for non-cleared tables, as clearing removes the
63 // deleted elements count.
64 // [kPrefixSize + 3 + NumberOfDeletedElements()..length]: Not used
65 template <class Derived, int entrysize>
66 class OrderedHashTable : public FixedArray {
67 public:
68 // Returns an OrderedHashTable (possibly |table|) with enough space
69 // to add at least one new element.
70 static MaybeHandle<Derived> EnsureGrowable(Isolate* isolate,
71 Handle<Derived> table);
72
73 // Returns an OrderedHashTable (possibly |table|) that's shrunken
74 // if possible.
75 static Handle<Derived> Shrink(Isolate* isolate, Handle<Derived> table);
76
77 // Returns a new empty OrderedHashTable and records the clearing so that
78 // existing iterators can be updated.
79 static Handle<Derived> Clear(Isolate* isolate, Handle<Derived> table);
80
81 // Returns true if the OrderedHashTable contains the key
82 static bool HasKey(Isolate* isolate, Derived table, Object key);
83
84 // Returns whether a potential key |k| returned by KeyAt is a real
85 // key (meaning that it is not a hole).
86 static inline bool IsKey(ReadOnlyRoots roots, Object k);
87
88 // Returns a true value if the OrderedHashTable contains the key and
89 // the key has been deleted. This does not shrink the table.
90 static bool Delete(Isolate* isolate, Derived table, Object key);
91
92 InternalIndex FindEntry(Isolate* isolate, Object key);
93
94 Object SlowReverseLookup(Isolate* isolate, Object value);
95
NumberOfElements()96 int NumberOfElements() const {
97 return Smi::ToInt(get(NumberOfElementsIndex()));
98 }
99
NumberOfDeletedElements()100 int NumberOfDeletedElements() const {
101 return Smi::ToInt(get(NumberOfDeletedElementsIndex()));
102 }
103
104 // Returns the number of contiguous entries in the data table, starting at 0,
105 // that either are real entries or have been deleted.
UsedCapacity()106 int UsedCapacity() const {
107 return NumberOfElements() + NumberOfDeletedElements();
108 }
109
NumberOfBuckets()110 int NumberOfBuckets() const {
111 return Smi::ToInt(get(NumberOfBucketsIndex()));
112 }
113
IterateEntries()114 InternalIndex::Range IterateEntries() {
115 return InternalIndex::Range(UsedCapacity());
116 }
117
118 // use IsKey to check if this is a deleted entry.
KeyAt(InternalIndex entry)119 Object KeyAt(InternalIndex entry) {
120 DCHECK_LT(entry.as_int(), this->UsedCapacity());
121 return get(EntryToIndex(entry));
122 }
123
124 // Similar to KeyAt, but indicates whether the given entry is valid
125 // (not deleted one)
126 bool ToKey(ReadOnlyRoots roots, InternalIndex entry, Object* out_key);
127
IsObsolete()128 bool IsObsolete() { return !get(NextTableIndex()).IsSmi(); }
129
130 // The next newer table. This is only valid if the table is obsolete.
NextTable()131 Derived NextTable() { return Derived::cast(get(NextTableIndex())); }
132
133 // When the table is obsolete we store the indexes of the removed holes.
RemovedIndexAt(int index)134 int RemovedIndexAt(int index) {
135 return Smi::ToInt(get(RemovedHolesIndex() + index));
136 }
137
138 // The extra +1 is for linking the bucket chains together.
139 static const int kEntrySize = entrysize + 1;
140 static const int kEntrySizeWithoutChain = entrysize;
141 static const int kChainOffset = entrysize;
142
143 static const int kNotFound = -1;
144 // The minimum capacity. Note that despite this value, 0 is also a permitted
145 // capacity, indicating a table without any storage for elements.
146 static const int kInitialCapacity = 4;
147
PrefixIndex()148 static constexpr int PrefixIndex() { return 0; }
149
NumberOfElementsIndex()150 static constexpr int NumberOfElementsIndex() { return Derived::kPrefixSize; }
151
152 // The next table is stored at the same index as the nof elements.
NextTableIndex()153 static constexpr int NextTableIndex() { return NumberOfElementsIndex(); }
154
NumberOfDeletedElementsIndex()155 static constexpr int NumberOfDeletedElementsIndex() {
156 return NumberOfElementsIndex() + 1;
157 }
158
NumberOfBucketsIndex()159 static constexpr int NumberOfBucketsIndex() {
160 return NumberOfDeletedElementsIndex() + 1;
161 }
162
HashTableStartIndex()163 static constexpr int HashTableStartIndex() {
164 return NumberOfBucketsIndex() + 1;
165 }
166
RemovedHolesIndex()167 static constexpr int RemovedHolesIndex() { return HashTableStartIndex(); }
168
NumberOfElementsOffset()169 static constexpr int NumberOfElementsOffset() {
170 return FixedArray::OffsetOfElementAt(NumberOfElementsIndex());
171 }
172
NextTableOffset()173 static constexpr int NextTableOffset() {
174 return FixedArray::OffsetOfElementAt(NextTableIndex());
175 }
176
NumberOfDeletedElementsOffset()177 static constexpr int NumberOfDeletedElementsOffset() {
178 return FixedArray::OffsetOfElementAt(NumberOfDeletedElementsIndex());
179 }
180
NumberOfBucketsOffset()181 static constexpr int NumberOfBucketsOffset() {
182 return FixedArray::OffsetOfElementAt(NumberOfBucketsIndex());
183 }
184
HashTableStartOffset()185 static constexpr int HashTableStartOffset() {
186 return FixedArray::OffsetOfElementAt(HashTableStartIndex());
187 }
188
189 static const int kLoadFactor = 2;
190
191 // NumberOfDeletedElements is set to kClearedTableSentinel when
192 // the table is cleared, which allows iterator transitions to
193 // optimize that case.
194 static const int kClearedTableSentinel = -1;
MaxCapacity()195 static constexpr int MaxCapacity() {
196 return (FixedArray::kMaxLength - HashTableStartIndex()) /
197 (1 + (kEntrySize * kLoadFactor));
198 }
199
200 protected:
201 // Returns an OrderedHashTable with a capacity of at least |capacity|.
202 static MaybeHandle<Derived> Allocate(
203 Isolate* isolate, int capacity,
204 AllocationType allocation = AllocationType::kYoung);
205
206 static MaybeHandle<Derived> AllocateEmpty(Isolate* isolate,
207 AllocationType allocation,
208 RootIndex root_ndex);
209
210 static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table);
211 static MaybeHandle<Derived> Rehash(Isolate* isolate, Handle<Derived> table,
212 int new_capacity);
213
HashToEntryRaw(int hash)214 int HashToEntryRaw(int hash) {
215 int bucket = HashToBucket(hash);
216 Object entry = this->get(HashTableStartIndex() + bucket);
217 int entry_int = Smi::ToInt(entry);
218 DCHECK(entry_int == kNotFound || entry_int >= 0);
219 return entry_int;
220 }
221
NextChainEntryRaw(int entry)222 int NextChainEntryRaw(int entry) {
223 DCHECK_LT(entry, this->UsedCapacity());
224 Object next_entry = get(EntryToIndexRaw(entry) + kChainOffset);
225 int next_entry_int = Smi::ToInt(next_entry);
226 DCHECK(next_entry_int == kNotFound || next_entry_int >= 0);
227 return next_entry_int;
228 }
229
230 // Returns an index into |this| for the given entry.
EntryToIndexRaw(int entry)231 int EntryToIndexRaw(int entry) {
232 return HashTableStartIndex() + NumberOfBuckets() + (entry * kEntrySize);
233 }
234
EntryToIndex(InternalIndex entry)235 int EntryToIndex(InternalIndex entry) {
236 return EntryToIndexRaw(entry.as_int());
237 }
238
HashToBucket(int hash)239 int HashToBucket(int hash) { return hash & (NumberOfBuckets() - 1); }
240
SetNumberOfBuckets(int num)241 void SetNumberOfBuckets(int num) {
242 set(NumberOfBucketsIndex(), Smi::FromInt(num));
243 }
244
SetNumberOfElements(int num)245 void SetNumberOfElements(int num) {
246 set(NumberOfElementsIndex(), Smi::FromInt(num));
247 }
248
SetNumberOfDeletedElements(int num)249 void SetNumberOfDeletedElements(int num) {
250 set(NumberOfDeletedElementsIndex(), Smi::FromInt(num));
251 }
252
253 // Returns the number elements that can fit into the allocated buffer.
Capacity()254 int Capacity() { return NumberOfBuckets() * kLoadFactor; }
255
SetNextTable(Derived next_table)256 void SetNextTable(Derived next_table) { set(NextTableIndex(), next_table); }
257
SetRemovedIndexAt(int index,int removed_index)258 void SetRemovedIndexAt(int index, int removed_index) {
259 return set(RemovedHolesIndex() + index, Smi::FromInt(removed_index));
260 }
261
262 OBJECT_CONSTRUCTORS(OrderedHashTable, FixedArray);
263
264 private:
265 friend class OrderedNameDictionaryHandler;
266 };
267
268 class V8_EXPORT_PRIVATE OrderedHashSet
269 : public OrderedHashTable<OrderedHashSet, 1> {
270 using Base = OrderedHashTable<OrderedHashSet, 1>;
271
272 public:
273 DECL_CAST(OrderedHashSet)
274
275 static MaybeHandle<OrderedHashSet> Add(Isolate* isolate,
276 Handle<OrderedHashSet> table,
277 Handle<Object> value);
278 static Handle<FixedArray> ConvertToKeysArray(Isolate* isolate,
279 Handle<OrderedHashSet> table,
280 GetKeysConversion convert);
281 static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
282 Handle<OrderedHashSet> table,
283 int new_capacity);
284 static MaybeHandle<OrderedHashSet> Rehash(Isolate* isolate,
285 Handle<OrderedHashSet> table);
286 static MaybeHandle<OrderedHashSet> Allocate(
287 Isolate* isolate, int capacity,
288 AllocationType allocation = AllocationType::kYoung);
289
290 static MaybeHandle<OrderedHashSet> AllocateEmpty(
291 Isolate* isolate, AllocationType allocation = AllocationType::kReadOnly);
292
293 static HeapObject GetEmpty(ReadOnlyRoots ro_roots);
294 static inline Handle<Map> GetMap(ReadOnlyRoots roots);
295 static inline bool Is(Handle<HeapObject> table);
296 static const int kPrefixSize = 0;
297
298 OBJECT_CONSTRUCTORS(OrderedHashSet, OrderedHashTable<OrderedHashSet, 1>);
299 };
300
301 class V8_EXPORT_PRIVATE OrderedHashMap
302 : public OrderedHashTable<OrderedHashMap, 2> {
303 using Base = OrderedHashTable<OrderedHashMap, 2>;
304
305 public:
306 DECL_CAST(OrderedHashMap)
307
308 // Returns a value if the OrderedHashMap contains the key, otherwise
309 // returns undefined.
310 static MaybeHandle<OrderedHashMap> Add(Isolate* isolate,
311 Handle<OrderedHashMap> table,
312 Handle<Object> key,
313 Handle<Object> value);
314
315 static MaybeHandle<OrderedHashMap> Allocate(
316 Isolate* isolate, int capacity,
317 AllocationType allocation = AllocationType::kYoung);
318
319 static MaybeHandle<OrderedHashMap> AllocateEmpty(
320 Isolate* isolate, AllocationType allocation = AllocationType::kReadOnly);
321
322 static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
323 Handle<OrderedHashMap> table,
324 int new_capacity);
325 static MaybeHandle<OrderedHashMap> Rehash(Isolate* isolate,
326 Handle<OrderedHashMap> table);
327 Object ValueAt(InternalIndex entry);
328
329 // This takes and returns raw Address values containing tagged Object
330 // pointers because it is called via ExternalReference.
331 static Address GetHash(Isolate* isolate, Address raw_key);
332
333 static HeapObject GetEmpty(ReadOnlyRoots ro_roots);
334 static inline Handle<Map> GetMap(ReadOnlyRoots roots);
335 static inline bool Is(Handle<HeapObject> table);
336
337 static const int kValueOffset = 1;
338 static const int kPrefixSize = 0;
339
340 OBJECT_CONSTRUCTORS(OrderedHashMap, OrderedHashTable<OrderedHashMap, 2>);
341 };
342
343 // This is similar to the OrderedHashTable, except for the memory
344 // layout where we use byte instead of Smi. The max capacity of this
345 // is only 254, we transition to an OrderedHashTable beyond that
346 // limit.
347 //
348 // Each bucket and chain value is a byte long. The padding exists so
349 // that the DataTable entries start aligned. A bucket or chain value
350 // of 255 is used to denote an unknown entry.
351 //
352 // The prefix size is calculated as the kPrefixSize * kTaggedSize.
353 //
354 // Memory layout: [ Prefix ] [ Header ] [ Padding ] [ DataTable ] [ HashTable ]
355 // [ Chains ]
356 //
357 // The index are represented as bytes, on a 64 bit machine with
358 // kEntrySize = 1, capacity = 4 and entries = 2:
359 //
360 // [ 0 ] : Prefix
361 //
362 // Note: For the sake of brevity, the following start with index 0
363 // but, they actually start from kPrefixSize * kTaggedSize to
364 // account for the the prefix.
365 //
366 // [ Header ] :
367 // [0] : Number of elements
368 // [1] : Number of deleted elements
369 // [2] : Number of buckets
370 //
371 // [ Padding ] :
372 // [3 .. 7] : Padding
373 //
374 // [ DataTable ] :
375 // [8 .. 15] : Entry 1
376 // [16 .. 23] : Entry 2
377 // [24 .. 31] : empty
378 // [32 .. 39] : empty
379 //
380 // [ HashTable ] :
381 // [40] : First chain-link for bucket 1
382 // [41] : empty
383 //
384 // [ Chains ] :
385 // [42] : Next chain link for bucket 1
386 // [43] : empty
387 // [44] : empty
388 // [45] : empty
389 //
390 template <class Derived>
391 class SmallOrderedHashTable : public HeapObject {
392 public:
393 // Offset points to a relative location in the table
394 using Offset = int;
395
396 // ByteIndex points to a index in the table that needs to be
397 // converted to an Offset.
398 using ByteIndex = int;
399
400 void Initialize(Isolate* isolate, int capacity);
401
402 static Handle<Derived> Allocate(
403 Isolate* isolate, int capacity,
404 AllocationType allocation = AllocationType::kYoung);
405
406 // Returns a true if the OrderedHashTable contains the key
407 bool HasKey(Isolate* isolate, Handle<Object> key);
408
409 // Returns a true value if the table contains the key and
410 // the key has been deleted. This does not shrink the table.
411 static bool Delete(Isolate* isolate, Derived table, Object key);
412
413 // Returns an SmallOrderedHashTable (possibly |table|) with enough
414 // space to add at least one new element. Returns empty handle if
415 // we've already reached MaxCapacity.
416 static MaybeHandle<Derived> Grow(Isolate* isolate, Handle<Derived> table);
417
418 InternalIndex FindEntry(Isolate* isolate, Object key);
419 static Handle<Derived> Shrink(Isolate* isolate, Handle<Derived> table);
420
421 // Iterates only fields in the DataTable.
422 class BodyDescriptor;
423
424 // Returns total size in bytes required for a table of given
425 // capacity.
SizeFor(int capacity)426 static int SizeFor(int capacity) {
427 DCHECK_GE(capacity, kMinCapacity);
428 DCHECK_LE(capacity, kMaxCapacity);
429
430 int data_table_size = DataTableSizeFor(capacity);
431 int hash_table_size = capacity / kLoadFactor;
432 int chain_table_size = capacity;
433 int total_size = DataTableStartOffset() + data_table_size +
434 hash_table_size + chain_table_size;
435
436 return RoundUp(total_size, kTaggedSize);
437 }
438
439 // Returns the number elements that can fit into the allocated table.
Capacity()440 int Capacity() const {
441 int capacity = NumberOfBuckets() * kLoadFactor;
442 DCHECK_GE(capacity, kMinCapacity);
443 DCHECK_LE(capacity, kMaxCapacity);
444
445 return capacity;
446 }
447
448 // Returns the number elements that are present in the table.
NumberOfElements()449 int NumberOfElements() const {
450 int nof_elements = getByte(NumberOfElementsOffset(), 0);
451 DCHECK_LE(nof_elements, Capacity());
452
453 return nof_elements;
454 }
455
NumberOfDeletedElements()456 int NumberOfDeletedElements() const {
457 int nof_deleted_elements = getByte(NumberOfDeletedElementsOffset(), 0);
458 DCHECK_LE(nof_deleted_elements, Capacity());
459
460 return nof_deleted_elements;
461 }
462
NumberOfBuckets()463 int NumberOfBuckets() const { return getByte(NumberOfBucketsOffset(), 0); }
464
465 V8_INLINE Object KeyAt(InternalIndex entry) const;
466
IterateEntries()467 InternalIndex::Range IterateEntries() {
468 return InternalIndex::Range(UsedCapacity());
469 }
470
471 DECL_VERIFIER(SmallOrderedHashTable)
472
473 static const int kMinCapacity = 4;
474 static const byte kNotFound = 0xFF;
475
476 // We use the value 255 to indicate kNotFound for chain and bucket
477 // values, which means that this value can't be used a valid
478 // index.
479 static const int kMaxCapacity = 254;
480 STATIC_ASSERT(kMaxCapacity < kNotFound);
481
482 // The load factor is used to derive the number of buckets from
483 // capacity during Allocation. We also depend on this to calaculate
484 // the capacity from number of buckets after allocation. If we
485 // decide to change kLoadFactor to something other than 2, capacity
486 // should be stored as another field of this object.
487 static const int kLoadFactor = 2;
488
489 // Our growth strategy involves doubling the capacity until we reach
490 // kMaxCapacity, but since the kMaxCapacity is always less than 256,
491 // we will never fully utilize this table. We special case for 256,
492 // by changing the new capacity to be kMaxCapacity in
493 // SmallOrderedHashTable::Grow.
494 static const int kGrowthHack = 256;
495
496 protected:
497 static Handle<Derived> Rehash(Isolate* isolate, Handle<Derived> table,
498 int new_capacity);
499
500 void SetDataEntry(int entry, int relative_index, Object value);
501
502 // TODO(gsathya): Calculate all the various possible values for this
503 // at compile time since capacity can only be 4 different values.
GetBucketsStartOffset()504 Offset GetBucketsStartOffset() const {
505 int capacity = Capacity();
506 int data_table_size = DataTableSizeFor(capacity);
507 return DataTableStartOffset() + data_table_size;
508 }
509
GetHashTableStartAddress(int capacity)510 Address GetHashTableStartAddress(int capacity) const {
511 return field_address(DataTableStartOffset() + DataTableSizeFor(capacity));
512 }
513
SetFirstEntry(int bucket,byte value)514 void SetFirstEntry(int bucket, byte value) {
515 DCHECK_LE(static_cast<unsigned>(bucket), NumberOfBuckets());
516 setByte(GetBucketsStartOffset(), bucket, value);
517 }
518
GetFirstEntry(int bucket)519 int GetFirstEntry(int bucket) const {
520 DCHECK_LE(static_cast<unsigned>(bucket), NumberOfBuckets());
521 return getByte(GetBucketsStartOffset(), bucket);
522 }
523
524 // TODO(gsathya): Calculate all the various possible values for this
525 // at compile time since capacity can only be 4 different values.
GetChainTableOffset()526 Offset GetChainTableOffset() const {
527 int nof_buckets = NumberOfBuckets();
528 int capacity = nof_buckets * kLoadFactor;
529 DCHECK_EQ(Capacity(), capacity);
530
531 int data_table_size = DataTableSizeFor(capacity);
532 int hash_table_size = nof_buckets;
533 return DataTableStartOffset() + data_table_size + hash_table_size;
534 }
535
SetNextEntry(int entry,int next_entry)536 void SetNextEntry(int entry, int next_entry) {
537 DCHECK_LT(static_cast<unsigned>(entry), Capacity());
538 DCHECK_GE(static_cast<unsigned>(next_entry), 0);
539 DCHECK(next_entry <= Capacity() || next_entry == kNotFound);
540 setByte(GetChainTableOffset(), entry, next_entry);
541 }
542
GetNextEntry(int entry)543 int GetNextEntry(int entry) const {
544 DCHECK_LT(entry, Capacity());
545 return getByte(GetChainTableOffset(), entry);
546 }
547
548 V8_INLINE Object GetDataEntry(int entry, int relative_index);
549
HashToBucket(int hash)550 int HashToBucket(int hash) const { return hash & (NumberOfBuckets() - 1); }
551
HashToFirstEntry(int hash)552 int HashToFirstEntry(int hash) const {
553 int bucket = HashToBucket(hash);
554 int entry = GetFirstEntry(bucket);
555 DCHECK(entry < Capacity() || entry == kNotFound);
556 return entry;
557 }
558
SetNumberOfBuckets(int num)559 void SetNumberOfBuckets(int num) { setByte(NumberOfBucketsOffset(), 0, num); }
560
SetNumberOfElements(int num)561 void SetNumberOfElements(int num) {
562 DCHECK_LE(static_cast<unsigned>(num), Capacity());
563 setByte(NumberOfElementsOffset(), 0, num);
564 }
565
SetNumberOfDeletedElements(int num)566 void SetNumberOfDeletedElements(int num) {
567 DCHECK_LE(static_cast<unsigned>(num), Capacity());
568 setByte(NumberOfDeletedElementsOffset(), 0, num);
569 }
570
PrefixOffset()571 static constexpr Offset PrefixOffset() { return kHeaderSize; }
572
NumberOfElementsOffset()573 static constexpr Offset NumberOfElementsOffset() {
574 return PrefixOffset() + (Derived::kPrefixSize * kTaggedSize);
575 }
576
NumberOfDeletedElementsOffset()577 static constexpr Offset NumberOfDeletedElementsOffset() {
578 return NumberOfElementsOffset() + kOneByteSize;
579 }
580
NumberOfBucketsOffset()581 static constexpr Offset NumberOfBucketsOffset() {
582 return NumberOfDeletedElementsOffset() + kOneByteSize;
583 }
584
PaddingOffset()585 static constexpr Offset PaddingOffset() {
586 return NumberOfBucketsOffset() + kOneByteSize;
587 }
588
PaddingSize()589 static constexpr size_t PaddingSize() {
590 return RoundUp<kTaggedSize>(PaddingOffset()) - PaddingOffset();
591 }
592
DataTableStartOffset()593 static constexpr Offset DataTableStartOffset() {
594 return PaddingOffset() + PaddingSize();
595 }
596
DataTableSizeFor(int capacity)597 static constexpr int DataTableSizeFor(int capacity) {
598 return capacity * Derived::kEntrySize * kTaggedSize;
599 }
600
601 // This is used for accessing the non |DataTable| part of the
602 // structure.
getByte(Offset offset,ByteIndex index)603 byte getByte(Offset offset, ByteIndex index) const {
604 DCHECK(offset < DataTableStartOffset() ||
605 offset >= GetBucketsStartOffset());
606 return ReadField<byte>(offset + (index * kOneByteSize));
607 }
608
setByte(Offset offset,ByteIndex index,byte value)609 void setByte(Offset offset, ByteIndex index, byte value) {
610 DCHECK(offset < DataTableStartOffset() ||
611 offset >= GetBucketsStartOffset());
612 WriteField<byte>(offset + (index * kOneByteSize), value);
613 }
614
GetDataEntryOffset(int entry,int relative_index)615 Offset GetDataEntryOffset(int entry, int relative_index) const {
616 DCHECK_LT(entry, Capacity());
617 int offset_in_datatable = entry * Derived::kEntrySize * kTaggedSize;
618 int offset_in_entry = relative_index * kTaggedSize;
619 return DataTableStartOffset() + offset_in_datatable + offset_in_entry;
620 }
621
UsedCapacity()622 int UsedCapacity() const {
623 int used = NumberOfElements() + NumberOfDeletedElements();
624 DCHECK_LE(used, Capacity());
625
626 return used;
627 }
628
629 private:
630 friend class OrderedHashMapHandler;
631 friend class OrderedHashSetHandler;
632 friend class OrderedNameDictionaryHandler;
633 friend class CodeStubAssembler;
634
635 OBJECT_CONSTRUCTORS(SmallOrderedHashTable, HeapObject);
636 };
637
638 class SmallOrderedHashSet : public SmallOrderedHashTable<SmallOrderedHashSet> {
639 public:
640 DECL_CAST(SmallOrderedHashSet)
641
642 DECL_PRINTER(SmallOrderedHashSet)
643 EXPORT_DECL_VERIFIER(SmallOrderedHashSet)
644
645 static const int kKeyIndex = 0;
646 static const int kEntrySize = 1;
647 static const int kPrefixSize = 0;
648
649 // Adds |value| to |table|, if the capacity isn't enough, a new
650 // table is created. The original |table| is returned if there is
651 // capacity to store |value| otherwise the new table is returned.
652 V8_EXPORT_PRIVATE static MaybeHandle<SmallOrderedHashSet> Add(
653 Isolate* isolate, Handle<SmallOrderedHashSet> table, Handle<Object> key);
654 V8_EXPORT_PRIVATE static bool Delete(Isolate* isolate,
655 SmallOrderedHashSet table, Object key);
656 V8_EXPORT_PRIVATE bool HasKey(Isolate* isolate, Handle<Object> key);
657
658 static inline bool Is(Handle<HeapObject> table);
659 static inline Handle<Map> GetMap(ReadOnlyRoots roots);
660 static Handle<SmallOrderedHashSet> Rehash(Isolate* isolate,
661 Handle<SmallOrderedHashSet> table,
662 int new_capacity);
663 OBJECT_CONSTRUCTORS(SmallOrderedHashSet,
664 SmallOrderedHashTable<SmallOrderedHashSet>);
665 };
666
667 STATIC_ASSERT(kSmallOrderedHashSetMinCapacity ==
668 SmallOrderedHashSet::kMinCapacity);
669
670 class SmallOrderedHashMap : public SmallOrderedHashTable<SmallOrderedHashMap> {
671 public:
672 DECL_CAST(SmallOrderedHashMap)
673
674 DECL_PRINTER(SmallOrderedHashMap)
675 EXPORT_DECL_VERIFIER(SmallOrderedHashMap)
676
677 static const int kKeyIndex = 0;
678 static const int kValueIndex = 1;
679 static const int kEntrySize = 2;
680 static const int kPrefixSize = 0;
681
682 // Adds |value| to |table|, if the capacity isn't enough, a new
683 // table is created. The original |table| is returned if there is
684 // capacity to store |value| otherwise the new table is returned.
685 V8_EXPORT_PRIVATE static MaybeHandle<SmallOrderedHashMap> Add(
686 Isolate* isolate, Handle<SmallOrderedHashMap> table, Handle<Object> key,
687 Handle<Object> value);
688 V8_EXPORT_PRIVATE static bool Delete(Isolate* isolate,
689 SmallOrderedHashMap table, Object key);
690 V8_EXPORT_PRIVATE bool HasKey(Isolate* isolate, Handle<Object> key);
691 static inline bool Is(Handle<HeapObject> table);
692 static inline Handle<Map> GetMap(ReadOnlyRoots roots);
693
694 static Handle<SmallOrderedHashMap> Rehash(Isolate* isolate,
695 Handle<SmallOrderedHashMap> table,
696 int new_capacity);
697
698 OBJECT_CONSTRUCTORS(SmallOrderedHashMap,
699 SmallOrderedHashTable<SmallOrderedHashMap>);
700 };
701
702 STATIC_ASSERT(kSmallOrderedHashMapMinCapacity ==
703 SmallOrderedHashMap::kMinCapacity);
704
705 // TODO(gsathya): Rename this to OrderedHashTable, after we rename
706 // OrderedHashTable to LargeOrderedHashTable. Also set up a
707 // OrderedHashSetBase class as a base class for the two tables and use
708 // that instead of a HeapObject here.
709 template <class SmallTable, class LargeTable>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)710 class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) OrderedHashTableHandler {
711 public:
712 using Entry = int;
713
714 static MaybeHandle<HeapObject> Allocate(Isolate* isolate, int capacity);
715 static bool Delete(Isolate* isolate, Handle<HeapObject> table,
716 Handle<Object> key);
717 static bool HasKey(Isolate* isolate, Handle<HeapObject> table,
718 Handle<Object> key);
719
720 // TODO(gsathya): Move this to OrderedHashTable
721 static const int OrderedHashTableMinSize =
722 SmallOrderedHashTable<SmallTable>::kGrowthHack << 1;
723 };
724
725 extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
726 OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>;
727
728 class V8_EXPORT_PRIVATE OrderedHashMapHandler
729 : public OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap> {
730 public:
731 static MaybeHandle<HeapObject> Add(Isolate* isolate, Handle<HeapObject> table,
732 Handle<Object> key, Handle<Object> value);
733 static MaybeHandle<OrderedHashMap> AdjustRepresentation(
734 Isolate* isolate, Handle<SmallOrderedHashMap> table);
735 };
736
737 extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
738 OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>;
739
740 class V8_EXPORT_PRIVATE OrderedHashSetHandler
741 : public OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet> {
742 public:
743 static MaybeHandle<HeapObject> Add(Isolate* isolate, Handle<HeapObject> table,
744 Handle<Object> key);
745 static MaybeHandle<OrderedHashSet> AdjustRepresentation(
746 Isolate* isolate, Handle<SmallOrderedHashSet> table);
747 };
748
749 class V8_EXPORT_PRIVATE OrderedNameDictionary
750 : public OrderedHashTable<OrderedNameDictionary, 3> {
751 using Base = OrderedHashTable<OrderedNameDictionary, 3>;
752
753 public:
754 DECL_CAST(OrderedNameDictionary)
755
756 static MaybeHandle<OrderedNameDictionary> Add(
757 Isolate* isolate, Handle<OrderedNameDictionary> table, Handle<Name> key,
758 Handle<Object> value, PropertyDetails details);
759
760 void SetEntry(InternalIndex entry, Object key, Object value,
761 PropertyDetails details);
762
763 InternalIndex FindEntry(Isolate* isolate, Object key);
764
765 int NumberOfEnumerableProperties();
766
767 Object SlowReverseLookup(Isolate* isolate, Object value);
768
769 static Handle<OrderedNameDictionary> DeleteEntry(
770 Isolate* isolate, Handle<OrderedNameDictionary> table,
771 InternalIndex entry);
772
773 static MaybeHandle<OrderedNameDictionary> Allocate(
774 Isolate* isolate, int capacity,
775 AllocationType allocation = AllocationType::kYoung);
776
777 static MaybeHandle<OrderedNameDictionary> AllocateEmpty(
778 Isolate* isolate, AllocationType allocation = AllocationType::kReadOnly);
779
780 static MaybeHandle<OrderedNameDictionary> Rehash(
781 Isolate* isolate, Handle<OrderedNameDictionary> table, int new_capacity);
782
783 // Returns the value for entry.
784 inline Object ValueAt(InternalIndex entry);
785
786 // Like KeyAt, but casts to Name
787 inline Name NameAt(InternalIndex entry);
788
789 // Set the value for entry.
790 inline void ValueAtPut(InternalIndex entry, Object value);
791
792 // Returns the property details for the property at entry.
793 inline PropertyDetails DetailsAt(InternalIndex entry);
794
795 // Set the details for entry.
796 inline void DetailsAtPut(InternalIndex entry, PropertyDetails value);
797
798 inline void SetHash(int hash);
799 inline int Hash();
800
801 static HeapObject GetEmpty(ReadOnlyRoots ro_roots);
802 static inline Handle<Map> GetMap(ReadOnlyRoots roots);
803 static inline bool Is(Handle<HeapObject> table);
804
805 static const int kValueOffset = 1;
806 static const int kPropertyDetailsOffset = 2;
807 static const int kPrefixSize = 1;
808
809 static const bool kIsOrderedDictionaryType = true;
810
811 OBJECT_CONSTRUCTORS(OrderedNameDictionary,
812 OrderedHashTable<OrderedNameDictionary, 3>);
813 };
814
815 extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
816 OrderedHashTableHandler<SmallOrderedNameDictionary, OrderedNameDictionary>;
817
818 class V8_EXPORT_PRIVATE OrderedNameDictionaryHandler
819 : public OrderedHashTableHandler<SmallOrderedNameDictionary,
820 OrderedNameDictionary> {
821 public:
822 static MaybeHandle<HeapObject> Add(Isolate* isolate, Handle<HeapObject> table,
823 Handle<Name> key, Handle<Object> value,
824 PropertyDetails details);
825 static Handle<HeapObject> Shrink(Isolate* isolate, Handle<HeapObject> table);
826
827 static Handle<HeapObject> DeleteEntry(Isolate* isolate,
828 Handle<HeapObject> table,
829 InternalIndex entry);
830 static InternalIndex FindEntry(Isolate* isolate, HeapObject table, Name key);
831 static void SetEntry(HeapObject table, InternalIndex entry, Object key,
832 Object value, PropertyDetails details);
833
834 // Returns the value for entry.
835 static Object ValueAt(HeapObject table, InternalIndex entry);
836
837 // Set the value for entry.
838 static void ValueAtPut(HeapObject table, InternalIndex entry, Object value);
839
840 // Returns the property details for the property at entry.
841 static PropertyDetails DetailsAt(HeapObject table, InternalIndex entry);
842
843 // Set the details for entry.
844 static void DetailsAtPut(HeapObject table, InternalIndex entry,
845 PropertyDetails value);
846
847 static Name KeyAt(HeapObject table, InternalIndex entry);
848
849 static void SetHash(HeapObject table, int hash);
850 static int Hash(HeapObject table);
851
852 static int NumberOfElements(HeapObject table);
853 static int Capacity(HeapObject table);
854
855 protected:
856 static MaybeHandle<OrderedNameDictionary> AdjustRepresentation(
857 Isolate* isolate, Handle<SmallOrderedNameDictionary> table);
858 };
859
860 class SmallOrderedNameDictionary
861 : public SmallOrderedHashTable<SmallOrderedNameDictionary> {
862 public:
863 DECL_CAST(SmallOrderedNameDictionary)
864
865 DECL_PRINTER(SmallOrderedNameDictionary)
866 DECL_VERIFIER(SmallOrderedNameDictionary)
867
868 // Returns the value for entry.
869 inline Object ValueAt(InternalIndex entry);
870
871 static Handle<SmallOrderedNameDictionary> Rehash(
872 Isolate* isolate, Handle<SmallOrderedNameDictionary> table,
873 int new_capacity);
874
875 V8_EXPORT_PRIVATE static Handle<SmallOrderedNameDictionary> DeleteEntry(
876 Isolate* isolate, Handle<SmallOrderedNameDictionary> table,
877 InternalIndex entry);
878
879 // Set the value for entry.
880 inline void ValueAtPut(InternalIndex entry, Object value);
881
882 // Returns the property details for the property at entry.
883 inline PropertyDetails DetailsAt(InternalIndex entry);
884
885 // Set the details for entry.
886 inline void DetailsAtPut(InternalIndex entry, PropertyDetails value);
887
888 inline void SetHash(int hash);
889 inline int Hash();
890
891 static const int kKeyIndex = 0;
892 static const int kValueIndex = 1;
893 static const int kPropertyDetailsIndex = 2;
894 static const int kEntrySize = 3;
895 static const int kPrefixSize = 1;
896
897 // Adds |value| to |table|, if the capacity isn't enough, a new
898 // table is created. The original |table| is returned if there is
899 // capacity to store |value| otherwise the new table is returned.
900 V8_EXPORT_PRIVATE static MaybeHandle<SmallOrderedNameDictionary> Add(
901 Isolate* isolate, Handle<SmallOrderedNameDictionary> table,
902 Handle<Name> key, Handle<Object> value, PropertyDetails details);
903
904 V8_EXPORT_PRIVATE void SetEntry(InternalIndex entry, Object key, Object value,
905 PropertyDetails details);
906
907 static inline Handle<Map> GetMap(ReadOnlyRoots roots);
908 static inline bool Is(Handle<HeapObject> table);
909
910 OBJECT_CONSTRUCTORS(SmallOrderedNameDictionary,
911 SmallOrderedHashTable<SmallOrderedNameDictionary>);
912 };
913
914 } // namespace internal
915 } // namespace v8
916
917 #include "src/objects/object-macros-undef.h"
918
919 #endif // V8_OBJECTS_ORDERED_HASH_TABLE_H_
920