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