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