v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
ordered-hash-table.h
Go to the documentation of this file.
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
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):
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>
68 public:
69 // Returns an OrderedHashTable (possibly |table|) with enough space
70 // to add at least one new element.
71 template <template <typename> typename HandleType>
72 requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
73 static HandleType<Derived>::MaybeType EnsureCapacityForAdding(
74 Isolate* isolate, HandleType<Derived> table);
75
76 // Returns an OrderedHashTable (possibly |table|) that's shrunken
77 // if possible.
78 template <template <typename> typename HandleType>
79 requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
80 static HandleType<Derived> Shrink(Isolate* isolate,
81 HandleType<Derived> table);
82
83 // Returns a new empty OrderedHashTable and records the clearing so that
84 // existing iterators can be updated.
85 static Handle<Derived> Clear(Isolate* isolate, Handle<Derived> table);
86
87 // Returns true if the OrderedHashTable contains the key
88 static bool HasKey(Isolate* isolate, Tagged<Derived> table,
90
91 // Returns whether a potential key |k| returned by KeyAt is a real
92 // key (meaning that it is not a hole).
93 static inline bool IsKey(ReadOnlyRoots roots, Tagged<Object> k);
94
95 // Returns a true value if the OrderedHashTable contains the key and
96 // the key has been deleted. This does not shrink the table.
97 static bool Delete(Isolate* isolate, Tagged<Derived> table,
99
101
102 int NumberOfElements() const {
104 }
105
109
110 // Returns the number of contiguous entries in the data table, starting at 0,
111 // that either are real entries or have been deleted.
112 int UsedCapacity() const {
114 }
115
117
118 int NumberOfBuckets() const {
120 }
121
125
126 // use IsKey to check if this is a deleted entry.
128 DCHECK_LT(entry.as_int(), this->UsedCapacity());
129 return get(EntryToIndex(entry));
130 }
131
132 // Similar to KeyAt, but indicates whether the given entry is valid
133 // (not deleted one)
134 inline bool ToKey(ReadOnlyRoots roots, InternalIndex entry,
135 Tagged<Object>* out_key);
136
137 bool IsObsolete() { return !IsSmi(get(NextTableIndex())); }
138
139 // The next newer table. This is only valid if the table is obsolete.
141
142 // When the table is obsolete we store the indexes of the removed holes.
143 int RemovedIndexAt(int index) {
144 return Smi::ToInt(get(RemovedHolesIndex() + index));
145 }
146
147 // The extra +1 is for linking the bucket chains together.
148 static const int kEntrySize = entrysize + 1;
149 static const int kEntrySizeWithoutChain = entrysize;
150 static const int kChainOffset = entrysize;
151
152 static const int kNotFound = -1;
153 // The minimum capacity. Note that despite this value, 0 is also a permitted
154 // capacity, indicating a table without any storage for elements.
155 static const int kInitialCapacity = 4;
156
157 static constexpr int PrefixIndex() { return 0; }
158
159 static constexpr int NumberOfElementsIndex() { return Derived::kPrefixSize; }
160
161 // The next table is stored at the same index as the nof elements.
162 static constexpr int NextTableIndex() { return NumberOfElementsIndex(); }
163
164 static constexpr int NumberOfDeletedElementsIndex() {
165 return NumberOfElementsIndex() + 1;
166 }
167
168 static constexpr int NumberOfBucketsIndex() {
169 return NumberOfDeletedElementsIndex() + 1;
170 }
171
172 static constexpr int HashTableStartIndex() {
173 return NumberOfBucketsIndex() + 1;
174 }
175
176 static constexpr int RemovedHolesIndex() { return HashTableStartIndex(); }
177
181
182 static constexpr int NextTableOffset() {
184 }
185
189
190 static constexpr int NumberOfBucketsOffset() {
192 }
193
194 static constexpr int HashTableStartOffset() {
196 }
197
198 static const int kLoadFactor = 2;
199
200 // NumberOfDeletedElements is set to kClearedTableSentinel when
201 // the table is cleared, which allows iterator transitions to
202 // optimize that case.
203 static const int kClearedTableSentinel = -1;
204 static constexpr int MaxCapacity() {
206 (1 + (kEntrySize * kLoadFactor));
207 }
208
209 protected:
210 // Returns an OrderedHashTable with a capacity of at least |capacity|.
212 Isolate* isolate, int capacity,
214
216 AllocationType allocation,
217 RootIndex root_ndex);
218
219 template <template <typename> typename HandleType>
220 requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
221 static HandleType<Derived>::MaybeType Rehash(Isolate* isolate,
222 HandleType<Derived> table);
223 template <template <typename> typename HandleType>
224 requires(std::is_convertible_v<HandleType<Derived>, DirectHandle<Derived>>)
225 static HandleType<Derived>::MaybeType Rehash(Isolate* isolate,
226 HandleType<Derived> table,
227 int new_capacity);
228
229 int HashToEntryRaw(int hash) {
230 int bucket = HashToBucket(hash);
231 Tagged<Object> entry = this->get(HashTableStartIndex() + bucket);
232 int entry_int = Smi::ToInt(entry);
233 DCHECK(entry_int == kNotFound || entry_int >= 0);
234 return entry_int;
235 }
236
237 int NextChainEntryRaw(int entry) {
238 DCHECK_LT(entry, this->UsedCapacity());
239 Tagged<Object> next_entry = get(EntryToIndexRaw(entry) + kChainOffset);
240 int next_entry_int = Smi::ToInt(next_entry);
241 DCHECK(next_entry_int == kNotFound || next_entry_int >= 0);
242 return next_entry_int;
243 }
244
245 // Returns an index into |this| for the given entry.
246 int EntryToIndexRaw(int entry) {
247 return HashTableStartIndex() + NumberOfBuckets() + (entry * kEntrySize);
248 }
249
251 return EntryToIndexRaw(entry.as_int());
252 }
253
254 int HashToBucket(int hash) { return hash & (NumberOfBuckets() - 1); }
255
256 void SetNumberOfBuckets(int num) {
258 }
259
260 void SetNumberOfElements(int num) {
262 }
263
267
268 void SetNextTable(Tagged<Derived> next_table) {
269 set(NextTableIndex(), next_table);
270 }
271
272 void SetRemovedIndexAt(int index, int removed_index) {
273 return set(RemovedHolesIndex() + index, Smi::FromInt(removed_index));
274 }
275
276 private:
278};
279
281 : public OrderedHashTable<OrderedHashSet, 1> {
283
284 public:
286
287 template <template <typename> typename HandleType>
288 requires(std::is_convertible_v<HandleType<OrderedHashSet>,
290 static HandleType<OrderedHashSet>::MaybeType Add(
291 Isolate* isolate, HandleType<OrderedHashSet> table,
293 static Handle<FixedArray> ConvertToKeysArray(Isolate* isolate,
295 GetKeysConversion convert);
296 template <template <typename> typename HandleType>
297 requires(std::is_convertible_v<HandleType<OrderedHashSet>,
299 static HandleType<OrderedHashSet>::MaybeType Rehash(
300 Isolate* isolate, HandleType<OrderedHashSet> table);
301 template <template <typename> typename HandleType>
302 requires(std::is_convertible_v<HandleType<OrderedHashSet>,
304 static HandleType<OrderedHashSet>::MaybeType Rehash(
305 Isolate* isolate, HandleType<OrderedHashSet> table, int new_capacity);
306
307 template <typename IsolateT>
308 static MaybeHandle<OrderedHashSet> Allocate(
309 IsolateT* isolate, int capacity,
310 AllocationType allocation = AllocationType::kYoung);
311
312 static MaybeHandle<OrderedHashSet> AllocateEmpty(
313 Isolate* isolate, AllocationType allocation = AllocationType::kReadOnly);
314
315 static Tagged<HeapObject> GetEmpty(ReadOnlyRoots ro_roots);
316 static inline Handle<Map> GetMap(RootsTable& roots);
317 static inline bool Is(DirectHandle<HeapObject> table);
318 static const int kPrefixSize = 0;
319};
320
322 : public OrderedHashTable<OrderedHashMap, 2> {
324
325 public:
327
328 // Returns a value if the OrderedHashMap contains the key, otherwise
329 // returns undefined.
330 static MaybeHandle<OrderedHashMap> Add(Isolate* isolate,
334
335 template <typename IsolateT>
336 static MaybeHandle<OrderedHashMap> Allocate(
337 IsolateT* isolate, int capacity,
338 AllocationType allocation = AllocationType::kYoung);
339
340 static MaybeHandle<OrderedHashMap> AllocateEmpty(
341 Isolate* isolate, AllocationType allocation = AllocationType::kReadOnly);
342
343 template <template <typename> typename HandleType>
344 requires(std::is_convertible_v<HandleType<OrderedHashMap>,
346 static HandleType<OrderedHashMap>::MaybeType Rehash(
347 Isolate* isolate, HandleType<OrderedHashMap> table);
348 template <template <typename> typename HandleType>
349 requires(std::is_convertible_v<HandleType<OrderedHashMap>,
351 static HandleType<OrderedHashMap>::MaybeType Rehash(
352 Isolate* isolate, HandleType<OrderedHashMap> table, int new_capacity);
353
354 void SetEntry(InternalIndex entry, Tagged<Object> key, Tagged<Object> value);
355
356 Tagged<Object> ValueAt(InternalIndex entry);
357
358 // This takes and returns raw Address values containing tagged Object
359 // pointers because it is called via ExternalReference.
360 static Address GetHash(Isolate* isolate, Address raw_key);
361
362 static Tagged<HeapObject> GetEmpty(ReadOnlyRoots ro_roots);
363 static inline Handle<Map> GetMap(RootsTable& roots);
364 static inline bool Is(DirectHandle<HeapObject> table);
365
366 static const int kValueOffset = 1;
367 static const int kPrefixSize = 0;
368};
369
370// This is similar to the OrderedHashTable, except for the memory
371// layout where we use byte instead of Smi. The max capacity of this
372// is only 254, we transition to an OrderedHashTable beyond that
373// limit.
374//
375// Each bucket and chain value is a byte long. The padding exists so
376// that the DataTable entries start aligned. A bucket or chain value
377// of 255 is used to denote an unknown entry.
378//
379// The prefix size is calculated as the kPrefixSize * kTaggedSize.
380//
381// Memory layout: [ Prefix ] [ Header ] [ Padding ] [ DataTable ] [ HashTable ]
382// [ Chains ]
383//
384// The index are represented as bytes, on a 64 bit machine with
385// kEntrySize = 1, capacity = 4 and entries = 2:
386//
387// [ 0 ] : Prefix
388//
389// Note: For the sake of brevity, the following start with index 0
390// but, they actually start from kPrefixSize * kTaggedSize to
391// account for the the prefix.
392//
393// [ Header ] :
394// [0] : Number of elements
395// [1] : Number of deleted elements
396// [2] : Number of buckets
397//
398// [ Padding ] :
399// [3 .. 7] : Padding
400//
401// [ DataTable ] :
402// [8 .. 15] : Entry 1
403// [16 .. 23] : Entry 2
404// [24 .. 31] : empty
405// [32 .. 39] : empty
406//
407// [ HashTable ] :
408// [40] : First chain-link for bucket 1
409// [41] : empty
410//
411// [ Chains ] :
412// [42] : Next chain link for bucket 1
413// [43] : empty
414// [44] : empty
415// [45] : empty
416//
417template <class Derived>
419 public:
420 // Offset points to a relative location in the table
421 using Offset = int;
422
423 // ByteIndex points to a index in the table that needs to be
424 // converted to an Offset.
425 using ByteIndex = int;
426
427 void Initialize(Isolate* isolate, int capacity);
428
430 Isolate* isolate, int capacity,
432
433 // Returns a true if the OrderedHashTable contains the key
435
436 // Returns a true value if the table contains the key and
437 // the key has been deleted. This does not shrink the table.
438 static bool Delete(Isolate* isolate, Tagged<Derived> table,
440
441 // Returns an SmallOrderedHashTable (possibly |table|) with enough
442 // space to add at least one new element. Returns empty handle if
443 // we've already reached MaxCapacity.
445
448
449 // Iterates only fields in the DataTable.
450 class BodyDescriptor;
451
452 // Returns total size in bytes required for a table of given
453 // capacity.
454 static int SizeFor(int capacity) {
455 DCHECK_GE(capacity, kMinCapacity);
456 DCHECK_LE(capacity, kMaxCapacity);
457
458 int data_table_size = DataTableSizeFor(capacity);
459 int hash_table_size = capacity / kLoadFactor;
460 int chain_table_size = capacity;
461 int total_size = DataTableStartOffset() + data_table_size +
462 hash_table_size + chain_table_size;
463
464 return RoundUp(total_size, kTaggedSize);
465 }
466
467 // Returns the number elements that can fit into the allocated table.
468 int Capacity() const {
469 int capacity = NumberOfBuckets() * kLoadFactor;
470 DCHECK_GE(capacity, kMinCapacity);
471 DCHECK_LE(capacity, kMaxCapacity);
472
473 return capacity;
474 }
475
476 // Returns the number elements that are present in the table.
477 int NumberOfElements() const {
478 int nof_elements = getByte(NumberOfElementsOffset(), 0);
479 DCHECK_LE(nof_elements, Capacity());
480
481 return nof_elements;
482 }
483
485 int nof_deleted_elements = getByte(NumberOfDeletedElementsOffset(), 0);
486 DCHECK_LE(nof_deleted_elements, Capacity());
487
488 return nof_deleted_elements;
489 }
490
491 int NumberOfBuckets() const { return getByte(NumberOfBucketsOffset(), 0); }
492
494
498
500
501 static const int kMinCapacity = 4;
502 static const uint8_t kNotFound = 0xFF;
503
504 // We use the value 255 to indicate kNotFound for chain and bucket
505 // values, which means that this value can't be used a valid
506 // index.
507 static const int kMaxCapacity = 254;
508 static_assert(kMaxCapacity < kNotFound);
509
510 // The load factor is used to derive the number of buckets from
511 // capacity during Allocation. We also depend on this to calaculate
512 // the capacity from number of buckets after allocation. If we
513 // decide to change kLoadFactor to something other than 2, capacity
514 // should be stored as another field of this object.
515 static const int kLoadFactor = 2;
516
517 // Our growth strategy involves doubling the capacity until we reach
518 // kMaxCapacity, but since the kMaxCapacity is always less than 256,
519 // we will never fully utilize this table. We special case for 256,
520 // by changing the new capacity to be kMaxCapacity in
521 // SmallOrderedHashTable::Grow.
522 static const int kGrowthHack = 256;
523
524 protected:
525 static Handle<Derived> Rehash(Isolate* isolate, Handle<Derived> table,
526 int new_capacity);
527
528 void SetDataEntry(int entry, int relative_index, Tagged<Object> value);
529
530 // TODO(gsathya): Calculate all the various possible values for this
531 // at compile time since capacity can only be 4 different values.
533 int capacity = Capacity();
534 int data_table_size = DataTableSizeFor(capacity);
535 return DataTableStartOffset() + data_table_size;
536 }
537
538 Address GetHashTableStartAddress(int capacity) const {
540 }
541
542 void SetFirstEntry(int bucket, uint8_t value) {
543 DCHECK_LE(static_cast<unsigned>(bucket), NumberOfBuckets());
544 setByte(GetBucketsStartOffset(), bucket, value);
545 }
546
547 int GetFirstEntry(int bucket) const {
548 DCHECK_LE(static_cast<unsigned>(bucket), NumberOfBuckets());
549 return getByte(GetBucketsStartOffset(), bucket);
550 }
551
552 // TODO(gsathya): Calculate all the various possible values for this
553 // at compile time since capacity can only be 4 different values.
555 int nof_buckets = NumberOfBuckets();
556 int capacity = nof_buckets * kLoadFactor;
557 DCHECK_EQ(Capacity(), capacity);
558
559 int data_table_size = DataTableSizeFor(capacity);
560 int hash_table_size = nof_buckets;
561 return DataTableStartOffset() + data_table_size + hash_table_size;
562 }
563
564 void SetNextEntry(int entry, int next_entry) {
565 DCHECK_LT(static_cast<unsigned>(entry), Capacity());
566 DCHECK_GE(static_cast<unsigned>(next_entry), 0);
567 DCHECK(next_entry <= Capacity() || next_entry == kNotFound);
568 setByte(GetChainTableOffset(), entry, next_entry);
569 }
570
571 int GetNextEntry(int entry) const {
572 DCHECK_LT(entry, Capacity());
573 return getByte(GetChainTableOffset(), entry);
574 }
575
576 V8_INLINE Tagged<Object> GetDataEntry(int entry, int relative_index);
577
578 int HashToBucket(int hash) const { return hash & (NumberOfBuckets() - 1); }
579
580 int HashToFirstEntry(int hash) const {
581 int bucket = HashToBucket(hash);
582 int entry = GetFirstEntry(bucket);
583 DCHECK(entry < Capacity() || entry == kNotFound);
584 return entry;
585 }
586
588
589 void SetNumberOfElements(int num) {
590 DCHECK_LE(static_cast<unsigned>(num), Capacity());
592 }
593
595 DCHECK_LE(static_cast<unsigned>(num), Capacity());
597 }
598
599 static constexpr Offset PrefixOffset() { return kHeaderSize; }
600
601 static constexpr Offset NumberOfElementsOffset() {
602 return PrefixOffset() + (Derived::kPrefixSize * kTaggedSize);
603 }
604
608
609 static constexpr Offset NumberOfBucketsOffset() {
611 }
612
613 static constexpr Offset PaddingOffset() {
615 }
616
617 static constexpr size_t PaddingSize() {
619 }
620
621 static constexpr Offset DataTableStartOffset() {
622 return PaddingOffset() + PaddingSize();
623 }
624
625 static constexpr int DataTableSizeFor(int capacity) {
626 return capacity * Derived::kEntrySize * kTaggedSize;
627 }
628
629 // This is used for accessing the non |DataTable| part of the
630 // structure.
631 uint8_t getByte(Offset offset, ByteIndex index) const {
634 return ReadField<uint8_t>(offset + (index * kOneByteSize));
635 }
636
637 void setByte(Offset offset, ByteIndex index, uint8_t value) {
640 WriteField<uint8_t>(offset + (index * kOneByteSize), value);
641 }
642
643 Offset GetDataEntryOffset(int entry, int relative_index) const {
644 DCHECK_LT(entry, Capacity());
645 int offset_in_datatable = entry * Derived::kEntrySize * kTaggedSize;
646 int offset_in_entry = relative_index * kTaggedSize;
647 return DataTableStartOffset() + offset_in_datatable + offset_in_entry;
648 }
649
650 int UsedCapacity() const {
652 DCHECK_LE(used, Capacity());
653
654 return used;
655 }
656
657 private:
661 friend class CodeStubAssembler;
662
664};
665
666class SmallOrderedHashSet : public SmallOrderedHashTable<SmallOrderedHashSet> {
667 public:
670
671 static const int kKeyIndex = 0;
672 static const int kEntrySize = 1;
673 static const int kPrefixSize = 0;
674
675 // Adds |value| to |table|, if the capacity isn't enough, a new
676 // table is created. The original |table| is returned if there is
677 // capacity to store |value| otherwise the new table is returned.
679 Isolate* isolate, Handle<SmallOrderedHashSet> table,
681 V8_EXPORT_PRIVATE static bool Delete(Isolate* isolate,
683 Tagged<Object> key);
685
686 static inline bool Is(DirectHandle<HeapObject> table);
687 static inline DirectHandle<Map> GetMap(RootsTable& roots);
688 static Handle<SmallOrderedHashSet> Rehash(Isolate* isolate,
690 int new_capacity);
693};
694
695static_assert(kSmallOrderedHashSetMinCapacity ==
697
699 public:
702
703 static const int kKeyIndex = 0;
704 static const int kValueIndex = 1;
705 static const int kEntrySize = 2;
706 static const int kPrefixSize = 0;
707
708 // Adds |value| to |table|, if the capacity isn't enough, a new
709 // table is created. The original |table| is returned if there is
710 // capacity to store |value| otherwise the new table is returned.
712 Isolate* isolate, Handle<SmallOrderedHashMap> table,
714 V8_EXPORT_PRIVATE static bool Delete(Isolate* isolate,
716 Tagged<Object> key);
718 static inline bool Is(DirectHandle<HeapObject> table);
719 static inline DirectHandle<Map> GetMap(RootsTable& roots);
720
721 static Handle<SmallOrderedHashMap> Rehash(Isolate* isolate,
723 int new_capacity);
724
727};
728
731
732// TODO(gsathya): Rename this to OrderedHashTable, after we rename
733// OrderedHashTable to LargeOrderedHashTable. Also set up a
734// OrderedHashSetBase class as a base class for the two tables and use
735// that instead of a HeapObject here.
736template <class SmallTable, class LargeTable>
737class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) OrderedHashTableHandler {
738 public:
739 using Entry = int;
740
741 static MaybeHandle<HeapObject> Allocate(Isolate* isolate, int capacity);
742 static bool Delete(Isolate* isolate, Handle<HeapObject> table,
744 static bool HasKey(Isolate* isolate, Handle<HeapObject> table,
746
747 // TODO(gsathya): Move this to OrderedHashTable
748 static const int OrderedHashTableMinSize =
750};
751
752extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
753 OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>;
754
756 : public OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap> {
757 public:
761 static MaybeHandle<OrderedHashMap> AdjustRepresentation(
763};
764
765extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
766 OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>;
767
769 : public OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet> {
770 public:
773 static MaybeHandle<OrderedHashSet> AdjustRepresentation(
775};
776
780
781 public:
783
787 PropertyDetails details);
788
789 void SetEntry(InternalIndex entry, Tagged<Object> key, Tagged<Object> value,
790 PropertyDetails details);
791
792 template <typename IsolateT>
793 InternalIndex FindEntry(IsolateT* isolate, Tagged<Object> key);
794
795 // This is to make the interfaces of NameDictionary::FindEntry and
796 // OrderedNameDictionary::FindEntry compatible.
797 // TODO(emrich) clean this up: NameDictionary uses Handle<Object>
798 // for FindEntry keys due to its Key typedef, but that's also used
799 // for adding, where we do need handles.
800 template <typename IsolateT>
802 return FindEntry(isolate, *key);
803 }
804
805 static Handle<OrderedNameDictionary> DeleteEntry(
807 InternalIndex entry);
808
810 Isolate* isolate, int capacity,
812
813 static MaybeHandle<OrderedNameDictionary> AllocateEmpty(
814 Isolate* isolate, AllocationType allocation = AllocationType::kReadOnly);
815
816 template <template <typename> typename HandleType>
817 requires(std::is_convertible_v<HandleType<OrderedNameDictionary>,
819 static HandleType<OrderedNameDictionary>::MaybeType Rehash(
820 Isolate* isolate, HandleType<OrderedNameDictionary> table,
821 int new_capacity);
822
823 // Returns the value for entry.
824 inline Tagged<Object> ValueAt(InternalIndex entry);
825
826 // Like KeyAt, but casts to Name
827 inline Tagged<Name> NameAt(InternalIndex entry);
828
829 // Set the value for entry.
830 inline void ValueAtPut(InternalIndex entry, Tagged<Object> value);
831
832 // Returns the property details for the property at entry.
833 inline PropertyDetails DetailsAt(InternalIndex entry);
834
835 // Set the details for entry.
836 inline void DetailsAtPut(InternalIndex entry, PropertyDetails value);
837
838 inline void SetHash(int hash);
839 inline int Hash();
840
842 static inline Handle<Map> GetMap(RootsTable& roots);
843 static inline bool Is(DirectHandle<HeapObject> table);
844
845 static const int kValueOffset = 1;
846 static const int kPropertyDetailsOffset = 2;
847 static const int kPrefixSize = 1;
848
849 static constexpr int HashIndex() { return PrefixIndex(); }
850
851 static const bool kIsOrderedDictionaryType = true;
852};
853
854extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
855 OrderedHashTableHandler<SmallOrderedNameDictionary, OrderedNameDictionary>;
856
858 : public OrderedHashTableHandler<SmallOrderedNameDictionary,
860 public:
864 PropertyDetails details);
866 Handle<HeapObject> table);
867
868 static DirectHandle<HeapObject> DeleteEntry(Isolate* isolate,
869 Handle<HeapObject> table,
870 InternalIndex entry);
871 static InternalIndex FindEntry(Isolate* isolate, Tagged<HeapObject> table,
873 static void SetEntry(Tagged<HeapObject> table, InternalIndex entry,
875 PropertyDetails details);
876
877 // Returns the value for entry.
878 static Tagged<Object> ValueAt(Tagged<HeapObject> table, InternalIndex entry);
879
880 // Set the value for entry.
881 static void ValueAtPut(Tagged<HeapObject> table, InternalIndex entry,
882 Tagged<Object> value);
883
884 // Returns the property details for the property at entry.
885 static PropertyDetails DetailsAt(Tagged<HeapObject> table,
886 InternalIndex entry);
887
888 // Set the details for entry.
889 static void DetailsAtPut(Tagged<HeapObject> table, InternalIndex entry,
890 PropertyDetails value);
891
893
894 static void SetHash(Tagged<HeapObject> table, int hash);
895 static int Hash(Tagged<HeapObject> table);
896
897 static int NumberOfElements(Tagged<HeapObject> table);
898 static int Capacity(Tagged<HeapObject> table);
899
900 protected:
901 static MaybeHandle<OrderedNameDictionary> AdjustRepresentation(
903};
904
906 : public SmallOrderedHashTable<SmallOrderedNameDictionary> {
907 public:
910
911 // Returns the value for entry.
912 inline Tagged<Object> ValueAt(InternalIndex entry);
913
916 int new_capacity);
917
920 InternalIndex entry);
921
922 // Set the value for entry.
923 inline void ValueAtPut(InternalIndex entry, Tagged<Object> value);
924
925 // Returns the property details for the property at entry.
926 inline PropertyDetails DetailsAt(InternalIndex entry);
927
928 // Set the details for entry.
929 inline void DetailsAtPut(InternalIndex entry, PropertyDetails value);
930
931 inline void SetHash(int hash);
932 inline int Hash();
933
934 static const int kKeyIndex = 0;
935 static const int kValueIndex = 1;
936 static const int kPropertyDetailsIndex = 2;
937 static const int kEntrySize = 3;
938 static const int kPrefixSize = 1;
939
940 // Adds |value| to |table|, if the capacity isn't enough, a new
941 // table is created. The original |table| is returned if there is
942 // capacity to store |value| otherwise the new table is returned.
946 PropertyDetails details);
947
949 Tagged<Object> value,
950 PropertyDetails details);
951
952 static inline DirectHandle<Map> GetMap(RootsTable& roots);
953 static inline bool Is(DirectHandle<HeapObject> table);
954
957};
958
959} // namespace internal
960} // namespace v8
961
963
964#endif // V8_OBJECTS_ORDERED_HASH_TABLE_H_
static constexpr int kMaxLength
static constexpr int kHeaderSize
Address field_address(size_t offset) const
T ReadField(size_t offset) const
void WriteField(size_t offset, T value) const
constexpr int as_int() const
static Handle< Map > GetMap(RootsTable &roots)
static constexpr int NextTableIndex()
static constexpr int MaxCapacity()
static HandleType< Derived > Shrink(Isolate *isolate, HandleType< Derived > table)
static MaybeHandle< Derived > Allocate(Isolate *isolate, int capacity, AllocationType allocation=AllocationType::kYoung)
Tagged< Object > KeyAt(InternalIndex entry)
void SetNextTable(Tagged< Derived > next_table)
bool ToKey(ReadOnlyRoots roots, InternalIndex entry, Tagged< Object > *out_key)
static constexpr int NextTableOffset()
static constexpr int RemovedHolesIndex()
static Handle< Derived > Clear(Isolate *isolate, Handle< Derived > table)
static bool IsKey(ReadOnlyRoots roots, Tagged< Object > k)
void SetRemovedIndexAt(int index, int removed_index)
static constexpr int NumberOfBucketsOffset()
static HandleType< Derived >::MaybeType EnsureCapacityForAdding(Isolate *isolate, HandleType< Derived > table)
int EntryToIndex(InternalIndex entry)
static constexpr int HashTableStartIndex()
static HandleType< Derived >::MaybeType Rehash(Isolate *isolate, HandleType< Derived > table)
static constexpr int HashTableStartOffset()
static MaybeHandle< Derived > AllocateEmpty(Isolate *isolate, AllocationType allocation, RootIndex root_ndex)
static constexpr int NumberOfElementsIndex()
InternalIndex::Range IterateEntries()
static bool HasKey(Isolate *isolate, Tagged< Derived > table, Tagged< Object > key)
static constexpr int NumberOfElementsOffset()
InternalIndex FindEntry(Isolate *isolate, Tagged< Object > key)
static constexpr int NumberOfDeletedElementsIndex()
static bool Delete(Isolate *isolate, Tagged< Derived > table, Tagged< Object > key)
static constexpr int NumberOfDeletedElementsOffset()
static constexpr int NumberOfBucketsIndex()
static constexpr int PrefixIndex()
static Tagged< HeapObject > GetEmpty(ReadOnlyRoots ro_roots)
InternalIndex FindEntry(IsolateT *isolate, DirectHandle< Object > key)
static DirectHandle< Map > GetMap(RootsTable &roots)
static V8_EXPORT_PRIVATE MaybeHandle< SmallOrderedHashSet > Add(Isolate *isolate, Handle< SmallOrderedHashSet > table, DirectHandle< Object > key)
V8_EXPORT_PRIVATE bool HasKey(Isolate *isolate, DirectHandle< Object > key)
static Handle< SmallOrderedHashSet > Rehash(Isolate *isolate, Handle< SmallOrderedHashSet > table, int new_capacity)
static V8_EXPORT_PRIVATE bool Delete(Isolate *isolate, Tagged< SmallOrderedHashSet > table, Tagged< Object > key)
static bool Is(DirectHandle< HeapObject > table)
static constexpr Offset NumberOfBucketsOffset()
V8_INLINE Tagged< Object > GetDataEntry(int entry, int relative_index)
static bool Delete(Isolate *isolate, Tagged< Derived > table, Tagged< Object > key)
V8_INLINE Tagged< Object > KeyAt(InternalIndex entry) const
static constexpr Offset PrefixOffset()
void SetFirstEntry(int bucket, uint8_t value)
Address GetHashTableStartAddress(int capacity) const
void SetDataEntry(int entry, int relative_index, Tagged< Object > value)
static Handle< Derived > Rehash(Isolate *isolate, Handle< Derived > table, int new_capacity)
static constexpr size_t PaddingSize()
static constexpr Offset DataTableStartOffset()
bool HasKey(Isolate *isolate, DirectHandle< Object > key)
InternalIndex FindEntry(Isolate *isolate, Tagged< Object > key)
void SetNextEntry(int entry, int next_entry)
static constexpr Offset PaddingOffset()
Offset GetDataEntryOffset(int entry, int relative_index) const
static MaybeHandle< Derived > Grow(Isolate *isolate, Handle< Derived > table)
static Handle< Derived > Allocate(Isolate *isolate, int capacity, AllocationType allocation=AllocationType::kYoung)
uint8_t getByte(Offset offset, ByteIndex index) const
static constexpr int DataTableSizeFor(int capacity)
static Handle< Derived > Shrink(Isolate *isolate, Handle< Derived > table)
void Initialize(Isolate *isolate, int capacity)
void setByte(Offset offset, ByteIndex index, uint8_t value)
OBJECT_CONSTRUCTORS(SmallOrderedHashTable, HeapObject)
static constexpr Offset NumberOfElementsOffset()
static constexpr Offset NumberOfDeletedElementsOffset()
OBJECT_CONSTRUCTORS(SmallOrderedNameDictionary, SmallOrderedHashTable< SmallOrderedNameDictionary >)
static constexpr int ToInt(const Tagged< Object > object)
Definition smi.h:33
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
void set(int index, Tagged< ElementT > value, WriteBarrierMode mode=kDefaultMode)
#define EXPORT_TEMPLATE_DECLARE(export)
int32_t offset
constexpr int kSmallOrderedHashMapMinCapacity
Definition globals.h:2772
constexpr int kTaggedSize
Definition globals.h:542
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
constexpr int kOneByteSize
Definition globals.h:703
GetKeysConversion
Definition keys.h:22
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
constexpr int kSmallOrderedHashSetMinCapacity
Definition globals.h:2771
static uint32_t Hash(RegisteredExtension *extension)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define DECL_VERIFIER(Name)
#define DECL_PRINTER(Name)
#define OBJECT_CONSTRUCTORS(Type,...)
#define EXPORT_DECL_VERIFIER(Name)
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr T RoundUp(T x, intptr_t m)
Definition macros.h:387
#define V8_EXPORT_PRIVATE
Definition macros.h:460
#define V8_INLINE
Definition v8config.h:500
std::unique_ptr< ValueMirror > key