• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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