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