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
20namespace v8 {
21namespace 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
66template <class Derived, int entrysize>
67class 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
96  int NumberOfElements() const {
97    return Smi::ToInt(get(NumberOfElementsIndex()));
98  }
99
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.
106  int UsedCapacity() const {
107    return NumberOfElements() + NumberOfDeletedElements();
108  }
109
110  int Capacity() { return NumberOfBuckets() * kLoadFactor; }
111
112  int NumberOfBuckets() const {
113    return Smi::ToInt(get(NumberOfBucketsIndex()));
114  }
115
116  InternalIndex::Range IterateEntries() {
117    return InternalIndex::Range(UsedCapacity());
118  }
119
120  // use IsKey to check if this is a deleted 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
130  bool IsObsolete() { return !get(NextTableIndex()).IsSmi(); }
131
132  // The next newer table. This is only valid if the table is obsolete.
133  Derived NextTable() { return Derived::cast(get(NextTableIndex())); }
134
135  // When the table is obsolete we store the indexes of the removed holes.
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
150  static constexpr int PrefixIndex() { return 0; }
151
152  static constexpr int NumberOfElementsIndex() { return Derived::kPrefixSize; }
153
154  // The next table is stored at the same index as the nof elements.
155  static constexpr int NextTableIndex() { return NumberOfElementsIndex(); }
156
157  static constexpr int NumberOfDeletedElementsIndex() {
158    return NumberOfElementsIndex() + 1;
159  }
160
161  static constexpr int NumberOfBucketsIndex() {
162    return NumberOfDeletedElementsIndex() + 1;
163  }
164
165  static constexpr int HashTableStartIndex() {
166    return NumberOfBucketsIndex() + 1;
167  }
168
169  static constexpr int RemovedHolesIndex() { return HashTableStartIndex(); }
170
171  static constexpr int NumberOfElementsOffset() {
172    return FixedArray::OffsetOfElementAt(NumberOfElementsIndex());
173  }
174
175  static constexpr int NextTableOffset() {
176    return FixedArray::OffsetOfElementAt(NextTableIndex());
177  }
178
179  static constexpr int NumberOfDeletedElementsOffset() {
180    return FixedArray::OffsetOfElementAt(NumberOfDeletedElementsIndex());
181  }
182
183  static constexpr int NumberOfBucketsOffset() {
184    return FixedArray::OffsetOfElementAt(NumberOfBucketsIndex());
185  }
186
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;
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
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
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.
236  int EntryToIndexRaw(int entry) {
237    return HashTableStartIndex() + NumberOfBuckets() + (entry * kEntrySize);
238  }
239
240  int EntryToIndex(InternalIndex entry) {
241    return EntryToIndexRaw(entry.as_int());
242  }
243
244  int HashToBucket(int hash) { return hash & (NumberOfBuckets() - 1); }
245
246  void SetNumberOfBuckets(int num) {
247    set(NumberOfBucketsIndex(), Smi::FromInt(num));
248  }
249
250  void SetNumberOfElements(int num) {
251    set(NumberOfElementsIndex(), Smi::FromInt(num));
252  }
253
254  void SetNumberOfDeletedElements(int num) {
255    set(NumberOfDeletedElementsIndex(), Smi::FromInt(num));
256  }
257
258
259  void SetNextTable(Derived next_table) { set(NextTableIndex(), next_table); }
260
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
271class 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
306class 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//
400template <class Derived>
401class 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.
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.
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.
459  int NumberOfElements() const {
460    int nof_elements = getByte(NumberOfElementsOffset(), 0);
461    DCHECK_LE(nof_elements, Capacity());
462
463    return nof_elements;
464  }
465
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
473  int NumberOfBuckets() const { return getByte(NumberOfBucketsOffset(), 0); }
474
475  V8_INLINE Object KeyAt(InternalIndex entry) const;
476
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.
514  Offset GetBucketsStartOffset() const {
515    int capacity = Capacity();
516    int data_table_size = DataTableSizeFor(capacity);
517    return DataTableStartOffset() + data_table_size;
518  }
519
520  Address GetHashTableStartAddress(int capacity) const {
521    return field_address(DataTableStartOffset() + DataTableSizeFor(capacity));
522  }
523
524  void SetFirstEntry(int bucket, byte value) {
525    DCHECK_LE(static_cast<unsigned>(bucket), NumberOfBuckets());
526    setByte(GetBucketsStartOffset(), bucket, value);
527  }
528
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.
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
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
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
560  int HashToBucket(int hash) const { return hash & (NumberOfBuckets() - 1); }
561
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
569  void SetNumberOfBuckets(int num) { setByte(NumberOfBucketsOffset(), 0, num); }
570
571  void SetNumberOfElements(int num) {
572    DCHECK_LE(static_cast<unsigned>(num), Capacity());
573    setByte(NumberOfElementsOffset(), 0, num);
574  }
575
576  void SetNumberOfDeletedElements(int num) {
577    DCHECK_LE(static_cast<unsigned>(num), Capacity());
578    setByte(NumberOfDeletedElementsOffset(), 0, num);
579  }
580
581  static constexpr Offset PrefixOffset() { return kHeaderSize; }
582
583  static constexpr Offset NumberOfElementsOffset() {
584    return PrefixOffset() + (Derived::kPrefixSize * kTaggedSize);
585  }
586
587  static constexpr Offset NumberOfDeletedElementsOffset() {
588    return NumberOfElementsOffset() + kOneByteSize;
589  }
590
591  static constexpr Offset NumberOfBucketsOffset() {
592    return NumberOfDeletedElementsOffset() + kOneByteSize;
593  }
594
595  static constexpr Offset PaddingOffset() {
596    return NumberOfBucketsOffset() + kOneByteSize;
597  }
598
599  static constexpr size_t PaddingSize() {
600    return RoundUp<kTaggedSize>(PaddingOffset()) - PaddingOffset();
601  }
602
603  static constexpr Offset DataTableStartOffset() {
604    return PaddingOffset() + PaddingSize();
605  }
606
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.
613  byte getByte(Offset offset, ByteIndex index) const {
614    DCHECK(offset < DataTableStartOffset() ||
615           offset >= GetBucketsStartOffset());
616    return ReadField<byte>(offset + (index * kOneByteSize));
617  }
618
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
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
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
648class 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
677STATIC_ASSERT(kSmallOrderedHashSetMinCapacity ==
678              SmallOrderedHashSet::kMinCapacity);
679
680class 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
712STATIC_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.
719template <class SmallTable, class LargeTable>
720class 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
735extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
736    OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>;
737
738class 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
747extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
748    OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>;
749
750class 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
759class 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>
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
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
838extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
839    OrderedHashTableHandler<SmallOrderedNameDictionary, OrderedNameDictionary>;
840
841class 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
883class 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