Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef VM_HASH_TABLE_H_ | 5 #ifndef VM_HASH_TABLE_H_ |
| 6 #define VM_HASH_TABLE_H_ | 6 #define VM_HASH_TABLE_H_ |
| 7 | 7 |
| 8 // Temporarily used when sorting the indices in EnumIndexHashTable. | 8 // Temporarily used when sorting the indices in EnumIndexHashTable. |
| 9 // TODO(koda): Remove these dependencies before using in production. | 9 // TODO(koda): Remove these dependencies before using in production. |
| 10 #include <map> | 10 #include <map> |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 80 // uword Hash(const Key& key) for any number of desired lookup key types. | 80 // uword Hash(const Key& key) for any number of desired lookup key types. |
| 81 // kPayloadSize: number of components of the payload in each entry. | 81 // kPayloadSize: number of components of the payload in each entry. |
| 82 // kMetaDataSize: number of elements reserved (e.g., for iteration order data). | 82 // kMetaDataSize: number of elements reserved (e.g., for iteration order data). |
| 83 template<typename KeyTraits, intptr_t kPayloadSize, intptr_t kMetaDataSize> | 83 template<typename KeyTraits, intptr_t kPayloadSize, intptr_t kMetaDataSize> |
| 84 class HashTable : public ValueObject { | 84 class HashTable : public ValueObject { |
| 85 public: | 85 public: |
| 86 typedef KeyTraits Traits; | 86 typedef KeyTraits Traits; |
| 87 // Uses 'isolate' for handle allocation. 'Release' must be called at the end | 87 // Uses 'isolate' for handle allocation. 'Release' must be called at the end |
| 88 // to obtain the final table after potential growth/shrinkage. | 88 // to obtain the final table after potential growth/shrinkage. |
| 89 HashTable(Isolate* isolate, RawArray* data) | 89 HashTable(Isolate* isolate, RawArray* data) |
| 90 : isolate_(isolate), data_(&Array::Handle(isolate_, data)) {} | 90 : isolate_(isolate), |
| 91 key_handle_(Object::Handle(isolate_)), | |
| 92 smi_handle_(Smi::Handle(isolate_)), | |
| 93 data_(&Array::Handle(isolate_, data)) {} | |
| 91 // Like above, except uses current isolate. | 94 // Like above, except uses current isolate. |
| 92 explicit HashTable(RawArray* data) | 95 explicit HashTable(RawArray* data) |
| 93 : isolate_(Isolate::Current()), data_(&Array::Handle(isolate_, data)) {} | 96 : isolate_(Isolate::Current()), |
| 97 key_handle_(Object::Handle(isolate_)), | |
| 98 smi_handle_(Smi::Handle(isolate_)), | |
| 99 data_(&Array::Handle(isolate_, data)) {} | |
| 94 | 100 |
| 95 Array& Release() { | 101 Array& Release() { |
| 96 ASSERT(data_ != NULL); | 102 ASSERT(data_ != NULL); |
| 97 Array* result = data_; | 103 Array* result = data_; |
| 98 // Ensure that no methods are called after 'Release'. | 104 // Ensure that no methods are called after 'Release'. |
| 99 data_ = NULL; | 105 data_ = NULL; |
| 100 return *result; | 106 return *result; |
| 101 } | 107 } |
| 102 | 108 |
| 103 ~HashTable() { | 109 ~HashTable() { |
| 104 // Ensure that 'Release' was called. | 110 // Ensure that 'Release' was called. |
| 105 ASSERT(data_ == NULL); | 111 ASSERT(data_ == NULL); |
| 106 } | 112 } |
| 107 | 113 |
| 108 // Returns a backing storage size such that 'num_occupied' distinct keys can | 114 // Returns a backing storage size such that 'num_occupied' distinct keys can |
| 109 // be inserted into the table. | 115 // be inserted into the table. |
| 110 static intptr_t ArrayLengthForNumOccupied(intptr_t num_occupied) { | 116 static intptr_t ArrayLengthForNumOccupied(intptr_t num_occupied) { |
| 111 // The current invariant requires at least one unoccupied entry. | 117 // The current invariant requires at least one unoccupied entry. |
| 112 // TODO(koda): Adjust if moving to quadratic probing. | 118 // TODO(koda): Adjust if moving to quadratic probing. |
| 113 intptr_t num_entries = num_occupied + 1; | 119 intptr_t num_entries = num_occupied + 1; |
| 114 return kFirstKeyIndex + (kEntrySize * num_entries); | 120 return kFirstKeyIndex + (kEntrySize * num_entries); |
| 115 } | 121 } |
| 116 | 122 |
| 117 // Initializes an empty table. | 123 // Initializes an empty table. |
| 118 void Initialize() const { | 124 void Initialize() const { |
| 119 ASSERT(data_->Length() >= ArrayLengthForNumOccupied(0)); | 125 ASSERT(data_->Length() >= ArrayLengthForNumOccupied(0)); |
| 120 Smi& zero = Smi::Handle(isolate(), Smi::New(0)); | 126 smi_handle_ = Smi::New(0); |
| 121 data_->SetAt(kOccupiedEntriesIndex, zero); | 127 data_->SetAt(kOccupiedEntriesIndex, smi_handle_); |
| 122 data_->SetAt(kDeletedEntriesIndex, zero); | 128 data_->SetAt(kDeletedEntriesIndex, smi_handle_); |
| 123 for (intptr_t i = kHeaderSize; i < data_->Length(); ++i) { | 129 for (intptr_t i = kHeaderSize; i < data_->Length(); ++i) { |
| 124 data_->SetAt(i, Object::sentinel()); | 130 data_->SetAt(i, Object::sentinel()); |
| 125 } | 131 } |
| 126 } | 132 } |
| 127 | 133 |
| 128 // Returns whether 'key' matches any key in the table. | 134 // Returns whether 'key' matches any key in the table. |
| 129 template<typename Key> | 135 template<typename Key> |
| 130 bool ContainsKey(const Key& key) const { | 136 bool ContainsKey(const Key& key) const { |
| 131 return FindKey(key) != -1; | 137 return FindKey(key) != -1; |
| 132 } | 138 } |
| 133 | 139 |
| 134 // Returns the entry that matches 'key', or -1 if none exists. | 140 // Returns the entry that matches 'key', or -1 if none exists. |
| 135 template<typename Key> | 141 template<typename Key> |
| 136 intptr_t FindKey(const Key& key) const { | 142 intptr_t FindKey(const Key& key) const { |
| 137 ASSERT(NumOccupied() < NumEntries()); | 143 ASSERT(NumOccupied() < NumEntries()); |
| 138 // TODO(koda): Add salt. | 144 // TODO(koda): Add salt. |
| 139 intptr_t probe = static_cast<uword>(KeyTraits::Hash(key)) % NumEntries(); | 145 intptr_t probe = static_cast<uword>(KeyTraits::Hash(key)) % NumEntries(); |
| 140 Object& obj = Object::Handle(isolate()); | |
| 141 // TODO(koda): Consider quadratic probing. | 146 // TODO(koda): Consider quadratic probing. |
| 142 for (; ; probe = (probe + 1) % NumEntries()) { | 147 for (; ; probe = (probe + 1) % NumEntries()) { |
| 143 if (IsUnused(probe)) { | 148 if (IsUnused(probe)) { |
| 144 return -1; | 149 return -1; |
| 145 } else if (IsDeleted(probe)) { | 150 } else if (IsDeleted(probe)) { |
| 146 continue; | 151 continue; |
| 147 } else { | 152 } else { |
| 148 obj = GetKey(probe); | 153 key_handle_ = GetKey(probe); |
| 149 if (KeyTraits::IsMatch(key, obj)) { | 154 if (KeyTraits::IsMatch(key, key_handle_)) { |
| 150 return probe; | 155 return probe; |
| 151 } | 156 } |
| 152 } | 157 } |
| 153 } | 158 } |
| 154 UNREACHABLE(); | 159 UNREACHABLE(); |
| 155 return -1; | 160 return -1; |
| 156 } | 161 } |
| 157 | 162 |
| 158 // Sets *entry to either: | 163 // Sets *entry to either: |
| 159 // - an occupied entry matching 'key', and returns true, or | 164 // - an occupied entry matching 'key', and returns true, or |
| 160 // - an unused/deleted entry where a matching key may be inserted, | 165 // - an unused/deleted entry where a matching key may be inserted, |
| 161 // and returns false. | 166 // and returns false. |
| 162 template<typename Key> | 167 template<typename Key> |
| 163 bool FindKeyOrDeletedOrUnused(const Key& key, intptr_t* entry) const { | 168 bool FindKeyOrDeletedOrUnused(const Key& key, intptr_t* entry) const { |
| 164 ASSERT(entry != NULL); | 169 ASSERT(entry != NULL); |
| 165 ASSERT(NumOccupied() < NumEntries()); | 170 ASSERT(NumOccupied() < NumEntries()); |
| 166 intptr_t probe = static_cast<uword>(KeyTraits::Hash(key)) % NumEntries(); | 171 intptr_t probe = static_cast<uword>(KeyTraits::Hash(key)) % NumEntries(); |
| 167 Object& obj = Object::Handle(isolate()); | |
| 168 intptr_t deleted = -1; | 172 intptr_t deleted = -1; |
| 169 // TODO(koda): Consider quadratic probing. | 173 // TODO(koda): Consider quadratic probing. |
| 170 for (; ; probe = (probe + 1) % NumEntries()) { | 174 for (; ; probe = (probe + 1) % NumEntries()) { |
| 171 if (IsUnused(probe)) { | 175 if (IsUnused(probe)) { |
| 172 *entry = (deleted != -1) ? deleted : probe; | 176 *entry = (deleted != -1) ? deleted : probe; |
| 173 return false; | 177 return false; |
| 174 } else if (IsDeleted(probe)) { | 178 } else if (IsDeleted(probe)) { |
| 175 if (deleted == -1) { | 179 if (deleted == -1) { |
| 176 deleted = probe; | 180 deleted = probe; |
| 177 } | 181 } |
| 178 } else { | 182 } else { |
| 179 obj = GetKey(probe); | 183 key_handle_ = GetKey(probe); |
| 180 if (KeyTraits::IsMatch(key, obj)) { | 184 if (KeyTraits::IsMatch(key, key_handle_)) { |
| 181 *entry = probe; | 185 *entry = probe; |
| 182 return true; | 186 return true; |
| 183 } | 187 } |
| 184 } | 188 } |
| 185 } | 189 } |
| 186 UNREACHABLE(); | 190 UNREACHABLE(); |
| 187 return false; | 191 return false; |
| 188 } | 192 } |
| 189 | 193 |
| 190 // Sets the key of a previously unoccupied entry. This must not be the last | 194 // Sets the key of a previously unoccupied entry. This must not be the last |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 } | 246 } |
| 243 intptr_t NumUnused() const { | 247 intptr_t NumUnused() const { |
| 244 return NumEntries() - NumOccupied() - NumDeleted(); | 248 return NumEntries() - NumOccupied() - NumDeleted(); |
| 245 } | 249 } |
| 246 intptr_t NumOccupied() const { | 250 intptr_t NumOccupied() const { |
| 247 return GetSmiValueAt(kOccupiedEntriesIndex); | 251 return GetSmiValueAt(kOccupiedEntriesIndex); |
| 248 } | 252 } |
| 249 intptr_t NumDeleted() const { | 253 intptr_t NumDeleted() const { |
| 250 return GetSmiValueAt(kDeletedEntriesIndex); | 254 return GetSmiValueAt(kDeletedEntriesIndex); |
| 251 } | 255 } |
| 256 Object* KeyHandle() const { | |
|
koda
2014/08/21 20:57:25
Does this need to return a pointer? You always see
siva
2014/08/21 23:54:30
Changed it to return the reference not a pointer.
| |
| 257 return &key_handle_; | |
| 258 } | |
| 259 Smi* SmiHandle() const { | |
| 260 return &smi_handle_; | |
| 261 } | |
| 252 | 262 |
| 253 protected: | 263 protected: |
| 254 static const intptr_t kOccupiedEntriesIndex = 0; | 264 static const intptr_t kOccupiedEntriesIndex = 0; |
| 255 static const intptr_t kDeletedEntriesIndex = 1; | 265 static const intptr_t kDeletedEntriesIndex = 1; |
| 256 static const intptr_t kHeaderSize = kDeletedEntriesIndex + 1; | 266 static const intptr_t kHeaderSize = kDeletedEntriesIndex + 1; |
| 257 static const intptr_t kMetaDataIndex = kHeaderSize; | 267 static const intptr_t kMetaDataIndex = kHeaderSize; |
| 258 static const intptr_t kFirstKeyIndex = kHeaderSize + kMetaDataSize; | 268 static const intptr_t kFirstKeyIndex = kHeaderSize + kMetaDataSize; |
| 259 static const intptr_t kEntrySize = 1 + kPayloadSize; | 269 static const intptr_t kEntrySize = 1 + kPayloadSize; |
| 260 | 270 |
| 261 intptr_t KeyIndex(intptr_t entry) const { | 271 intptr_t KeyIndex(intptr_t entry) const { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 275 void InternalSetKey(intptr_t entry, const Object& key) const { | 285 void InternalSetKey(intptr_t entry, const Object& key) const { |
| 276 data_->SetAt(KeyIndex(entry), key); | 286 data_->SetAt(KeyIndex(entry), key); |
| 277 } | 287 } |
| 278 | 288 |
| 279 intptr_t GetSmiValueAt(intptr_t index) const { | 289 intptr_t GetSmiValueAt(intptr_t index) const { |
| 280 ASSERT(Object::Handle(isolate(), data_->At(index)).IsSmi()); | 290 ASSERT(Object::Handle(isolate(), data_->At(index)).IsSmi()); |
| 281 return Smi::Value(Smi::RawCast(data_->At(index))); | 291 return Smi::Value(Smi::RawCast(data_->At(index))); |
| 282 } | 292 } |
| 283 | 293 |
| 284 void SetSmiValueAt(intptr_t index, intptr_t value) const { | 294 void SetSmiValueAt(intptr_t index, intptr_t value) const { |
| 285 const Smi& smi = Smi::Handle(isolate(), Smi::New(value)); | 295 smi_handle_ = Smi::New(value); |
| 286 data_->SetAt(index, smi); | 296 data_->SetAt(index, smi_handle_); |
| 287 } | 297 } |
| 288 | 298 |
| 289 void AdjustSmiValueAt(intptr_t index, intptr_t delta) const { | 299 void AdjustSmiValueAt(intptr_t index, intptr_t delta) const { |
| 290 SetSmiValueAt(index, (GetSmiValueAt(index) + delta)); | 300 SetSmiValueAt(index, (GetSmiValueAt(index) + delta)); |
| 291 } | 301 } |
| 292 | 302 |
| 293 Isolate* isolate() const { return isolate_; } | 303 Isolate* isolate() const { return isolate_; } |
| 294 | 304 |
| 295 Isolate* isolate_; | 305 Isolate* isolate_; |
| 306 Object& key_handle_; | |
| 307 Smi& smi_handle_; | |
| 296 // This is a pointer rather than a reference, to enable Release nulling it, | 308 // This is a pointer rather than a reference, to enable Release nulling it, |
| 297 // preventing post-Release modification. | 309 // preventing post-Release modification. |
| 298 Array* data_; | 310 Array* data_; |
| 299 | 311 |
| 300 friend class HashTables; | 312 friend class HashTables; |
| 301 }; | 313 }; |
| 302 | 314 |
| 303 | 315 |
| 304 // Table with unspecified iteration order. No payload overhead or metadata. | 316 // Table with unspecified iteration order. No payload overhead or metadata. |
| 305 template<typename KeyTraits, intptr_t kUserPayloadSize> | 317 template<typename KeyTraits, intptr_t kUserPayloadSize> |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 std::vector<intptr_t> entries_; | 396 std::vector<intptr_t> entries_; |
| 385 }; | 397 }; |
| 386 | 398 |
| 387 void Initialize() const { | 399 void Initialize() const { |
| 388 BaseTable::Initialize(); | 400 BaseTable::Initialize(); |
| 389 BaseTable::SetSmiValueAt(kNextEnumIndex, 0); | 401 BaseTable::SetSmiValueAt(kNextEnumIndex, 0); |
| 390 } | 402 } |
| 391 | 403 |
| 392 void InsertKey(intptr_t entry, const Object& key) const { | 404 void InsertKey(intptr_t entry, const Object& key) const { |
| 393 BaseTable::InsertKey(entry, key); | 405 BaseTable::InsertKey(entry, key); |
| 394 const Smi& next_enum_index = Smi::Handle(BaseTable::isolate(), | 406 *(BaseTable::SmiHandle()) = |
| 395 Smi::New(BaseTable::GetSmiValueAt(kNextEnumIndex))); | 407 Smi::New(BaseTable::GetSmiValueAt(kNextEnumIndex)); |
| 396 BaseTable::UpdatePayload(entry, kPayloadSize, next_enum_index); | 408 BaseTable::UpdatePayload(entry, |
| 409 kPayloadSize, | |
| 410 *(BaseTable::SmiHandle())); | |
| 397 // TODO(koda): Handle possible Smi overflow from repeated insert/delete. | 411 // TODO(koda): Handle possible Smi overflow from repeated insert/delete. |
| 398 BaseTable::AdjustSmiValueAt(kNextEnumIndex, 1); | 412 BaseTable::AdjustSmiValueAt(kNextEnumIndex, 1); |
| 399 } | 413 } |
| 400 | 414 |
| 401 // No extra book-keeping needed for DeleteEntry. | 415 // No extra book-keeping needed for DeleteEntry. |
| 402 }; | 416 }; |
| 403 | 417 |
| 404 | 418 |
| 405 class HashTables : public AllStatic { | 419 class HashTables : public AllStatic { |
| 406 public: | 420 public: |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 522 return BaseIterTable::GetPayload(entry, 0); | 536 return BaseIterTable::GetPayload(entry, 0); |
| 523 } | 537 } |
| 524 } | 538 } |
| 525 // Like InsertOrGetValue, but calls NewKey to allocate a key object if needed. | 539 // Like InsertOrGetValue, but calls NewKey to allocate a key object if needed. |
| 526 template<typename Key> | 540 template<typename Key> |
| 527 RawObject* InsertNewOrGetValue(const Key& key, | 541 RawObject* InsertNewOrGetValue(const Key& key, |
| 528 const Object& value_if_absent) const { | 542 const Object& value_if_absent) const { |
| 529 EnsureCapacity(); | 543 EnsureCapacity(); |
| 530 intptr_t entry = -1; | 544 intptr_t entry = -1; |
| 531 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { | 545 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
| 532 Object& new_key = Object::Handle(BaseIterTable::isolate(), | 546 *(BaseIterTable::KeyHandle()) = |
| 533 BaseIterTable::BaseTable::Traits::NewKey(key)); | 547 BaseIterTable::BaseTable::Traits::NewKey(key); |
| 534 BaseIterTable::InsertKey(entry, new_key); | 548 BaseIterTable::InsertKey(entry, *(BaseIterTable::KeyHandle())); |
| 535 BaseIterTable::UpdatePayload(entry, 0, value_if_absent); | 549 BaseIterTable::UpdatePayload(entry, 0, value_if_absent); |
| 536 return value_if_absent.raw(); | 550 return value_if_absent.raw(); |
| 537 } else { | 551 } else { |
| 538 return BaseIterTable::GetPayload(entry, 0); | 552 return BaseIterTable::GetPayload(entry, 0); |
| 539 } | 553 } |
| 540 } | 554 } |
| 541 | 555 |
| 542 template<typename Key> | 556 template<typename Key> |
| 543 bool Remove(const Key& key) const { | 557 bool Remove(const Key& key) const { |
| 544 intptr_t entry = BaseIterTable::FindKey(key); | 558 intptr_t entry = BaseIterTable::FindKey(key); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 603 return BaseIterTable::GetPayload(entry, 0); | 617 return BaseIterTable::GetPayload(entry, 0); |
| 604 } | 618 } |
| 605 } | 619 } |
| 606 | 620 |
| 607 // Like InsertOrGet, but calls NewKey to allocate a key object if needed. | 621 // Like InsertOrGet, but calls NewKey to allocate a key object if needed. |
| 608 template<typename Key> | 622 template<typename Key> |
| 609 RawObject* InsertNewOrGet(const Key& key) const { | 623 RawObject* InsertNewOrGet(const Key& key) const { |
| 610 EnsureCapacity(); | 624 EnsureCapacity(); |
| 611 intptr_t entry = -1; | 625 intptr_t entry = -1; |
| 612 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { | 626 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
| 613 Object& new_key = Object::Handle(BaseIterTable::isolate(), | 627 *(BaseIterTable::KeyHandle()) = |
| 614 BaseIterTable::BaseTable::Traits::NewKey(key)); | 628 BaseIterTable::BaseTable::Traits::NewKey(key); |
| 615 BaseIterTable::InsertKey(entry, new_key); | 629 BaseIterTable::InsertKey(entry, *(BaseIterTable::KeyHandle())); |
| 616 return new_key.raw(); | 630 return BaseIterTable::KeyHandle()->raw(); |
| 617 } else { | 631 } else { |
| 618 return BaseIterTable::GetKey(entry); | 632 return BaseIterTable::GetKey(entry); |
| 619 } | 633 } |
| 620 } | 634 } |
| 621 | 635 |
| 622 template<typename Key> | 636 template<typename Key> |
| 623 RawObject* GetOrNull(const Key& key, bool* present = NULL) const { | 637 RawObject* GetOrNull(const Key& key, bool* present = NULL) const { |
| 624 intptr_t entry = BaseIterTable::FindKey(key); | 638 intptr_t entry = BaseIterTable::FindKey(key); |
| 625 if (present != NULL) { | 639 if (present != NULL) { |
| 626 *present = (entry != -1); | 640 *present = (entry != -1); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 660 class EnumIndexHashSet : public HashSet<EnumIndexHashTable<KeyTraits, 0> > { | 674 class EnumIndexHashSet : public HashSet<EnumIndexHashTable<KeyTraits, 0> > { |
| 661 public: | 675 public: |
| 662 typedef HashSet<EnumIndexHashTable<KeyTraits, 0> > BaseSet; | 676 typedef HashSet<EnumIndexHashTable<KeyTraits, 0> > BaseSet; |
| 663 explicit EnumIndexHashSet(RawArray* data) : BaseSet(data) {} | 677 explicit EnumIndexHashSet(RawArray* data) : BaseSet(data) {} |
| 664 EnumIndexHashSet(Isolate* isolate, RawArray* data) : BaseSet(isolate, data) {} | 678 EnumIndexHashSet(Isolate* isolate, RawArray* data) : BaseSet(isolate, data) {} |
| 665 }; | 679 }; |
| 666 | 680 |
| 667 } // namespace dart | 681 } // namespace dart |
| 668 | 682 |
| 669 #endif // VM_HASH_TABLE_H_ | 683 #endif // VM_HASH_TABLE_H_ |
| OLD | NEW |