| 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 RUNTIME_VM_HASH_TABLE_H_ | 5 #ifndef RUNTIME_VM_HASH_TABLE_H_ |
| 6 #define RUNTIME_VM_HASH_TABLE_H_ | 6 #define RUNTIME_VM_HASH_TABLE_H_ |
| 7 | 7 |
| 8 #include "platform/assert.h" | 8 #include "platform/assert.h" |
| 9 #include "vm/object.h" | 9 #include "vm/object.h" |
| 10 | 10 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 // The header tracks the number of entries in each state. | 65 // The header tracks the number of entries in each state. |
| 66 // Any object except Object::sentinel() and Object::transition_sentinel() | 66 // Any object except Object::sentinel() and Object::transition_sentinel() |
| 67 // may be stored as a key. Any object may be stored in a payload. | 67 // may be stored as a key. Any object may be stored in a payload. |
| 68 // | 68 // |
| 69 // Parameters | 69 // Parameters |
| 70 // KeyTraits: defines static methods | 70 // KeyTraits: defines static methods |
| 71 // bool IsMatch(const Key& key, const Object& obj) and | 71 // bool IsMatch(const Key& key, const Object& obj) and |
| 72 // uword Hash(const Key& key) for any number of desired lookup key types. | 72 // uword Hash(const Key& key) for any number of desired lookup key types. |
| 73 // kPayloadSize: number of components of the payload in each entry. | 73 // kPayloadSize: number of components of the payload in each entry. |
| 74 // kMetaDataSize: number of elements reserved (e.g., for iteration order data). | 74 // kMetaDataSize: number of elements reserved (e.g., for iteration order data). |
| 75 template<typename KeyTraits, intptr_t kPayloadSize, intptr_t kMetaDataSize> | 75 template <typename KeyTraits, intptr_t kPayloadSize, intptr_t kMetaDataSize> |
| 76 class HashTable : public ValueObject { | 76 class HashTable : public ValueObject { |
| 77 public: | 77 public: |
| 78 typedef KeyTraits Traits; | 78 typedef KeyTraits Traits; |
| 79 // Uses the passed in handles for all handle operations. | 79 // Uses the passed in handles for all handle operations. |
| 80 // 'Release' must be called at the end to obtain the final table | 80 // 'Release' must be called at the end to obtain the final table |
| 81 // after potential growth/shrinkage. | 81 // after potential growth/shrinkage. |
| 82 HashTable(Object* key, Smi* index, Array* data) | 82 HashTable(Object* key, Smi* index, Array* data) |
| 83 : key_handle_(key), | 83 : key_handle_(key), |
| 84 smi_handle_(index), | 84 smi_handle_(index), |
| 85 data_(data), | 85 data_(data), |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 return kFirstKeyIndex + (kEntrySize * num_entries); | 120 return kFirstKeyIndex + (kEntrySize * num_entries); |
| 121 } | 121 } |
| 122 | 122 |
| 123 // Initializes an empty table. | 123 // Initializes an empty table. |
| 124 void Initialize() const { | 124 void Initialize() const { |
| 125 ASSERT(data_->Length() >= ArrayLengthForNumOccupied(0)); | 125 ASSERT(data_->Length() >= ArrayLengthForNumOccupied(0)); |
| 126 *smi_handle_ = Smi::New(0); | 126 *smi_handle_ = Smi::New(0); |
| 127 data_->SetAt(kOccupiedEntriesIndex, *smi_handle_); | 127 data_->SetAt(kOccupiedEntriesIndex, *smi_handle_); |
| 128 data_->SetAt(kDeletedEntriesIndex, *smi_handle_); | 128 data_->SetAt(kDeletedEntriesIndex, *smi_handle_); |
| 129 | 129 |
| 130 NOT_IN_PRODUCT( | 130 #if !defined(PRODUCT) |
| 131 data_->SetAt(kNumGrowsIndex, *smi_handle_); | 131 data_->SetAt(kNumGrowsIndex, *smi_handle_); |
| 132 data_->SetAt(kNumLT5LookupsIndex, *smi_handle_); | 132 data_->SetAt(kNumLT5LookupsIndex, *smi_handle_); |
| 133 data_->SetAt(kNumLT25LookupsIndex, *smi_handle_); | 133 data_->SetAt(kNumLT25LookupsIndex, *smi_handle_); |
| 134 data_->SetAt(kNumGT25LookupsIndex, *smi_handle_); | 134 data_->SetAt(kNumGT25LookupsIndex, *smi_handle_); |
| 135 ) // !PRODUCT | 135 #endif // !defined(PRODUCT) |
| 136 | 136 |
| 137 for (intptr_t i = kHeaderSize; i < data_->Length(); ++i) { | 137 for (intptr_t i = kHeaderSize; i < data_->Length(); ++i) { |
| 138 data_->SetAt(i, Object::sentinel()); | 138 data_->SetAt(i, Object::sentinel()); |
| 139 } | 139 } |
| 140 } | 140 } |
| 141 | 141 |
| 142 // Returns whether 'key' matches any key in the table. | 142 // Returns whether 'key' matches any key in the table. |
| 143 template<typename Key> | 143 template <typename Key> |
| 144 bool ContainsKey(const Key& key) const { | 144 bool ContainsKey(const Key& key) const { |
| 145 return FindKey(key) != -1; | 145 return FindKey(key) != -1; |
| 146 } | 146 } |
| 147 | 147 |
| 148 // Returns the entry that matches 'key', or -1 if none exists. | 148 // Returns the entry that matches 'key', or -1 if none exists. |
| 149 template<typename Key> | 149 template <typename Key> |
| 150 intptr_t FindKey(const Key& key) const { | 150 intptr_t FindKey(const Key& key) const { |
| 151 const intptr_t num_entries = NumEntries(); | 151 const intptr_t num_entries = NumEntries(); |
| 152 ASSERT(NumOccupied() < num_entries); | 152 ASSERT(NumOccupied() < num_entries); |
| 153 // TODO(koda): Add salt. | 153 // TODO(koda): Add salt. |
| 154 NOT_IN_PRODUCT(intptr_t collisions = 0;) | 154 NOT_IN_PRODUCT(intptr_t collisions = 0;) |
| 155 uword hash = KeyTraits::Hash(key); | 155 uword hash = KeyTraits::Hash(key); |
| 156 intptr_t probe = hash % num_entries; | 156 intptr_t probe = hash % num_entries; |
| 157 // TODO(koda): Consider quadratic probing. | 157 // TODO(koda): Consider quadratic probing. |
| 158 while (true) { | 158 while (true) { |
| 159 if (IsUnused(probe)) { | 159 if (IsUnused(probe)) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 172 probe = (probe == num_entries) ? 0 : probe; | 172 probe = (probe == num_entries) ? 0 : probe; |
| 173 } | 173 } |
| 174 UNREACHABLE(); | 174 UNREACHABLE(); |
| 175 return -1; | 175 return -1; |
| 176 } | 176 } |
| 177 | 177 |
| 178 // Sets *entry to either: | 178 // Sets *entry to either: |
| 179 // - an occupied entry matching 'key', and returns true, or | 179 // - an occupied entry matching 'key', and returns true, or |
| 180 // - an unused/deleted entry where a matching key may be inserted, | 180 // - an unused/deleted entry where a matching key may be inserted, |
| 181 // and returns false. | 181 // and returns false. |
| 182 template<typename Key> | 182 template <typename Key> |
| 183 bool FindKeyOrDeletedOrUnused(const Key& key, intptr_t* entry) const { | 183 bool FindKeyOrDeletedOrUnused(const Key& key, intptr_t* entry) const { |
| 184 const intptr_t num_entries = NumEntries(); | 184 const intptr_t num_entries = NumEntries(); |
| 185 ASSERT(entry != NULL); | 185 ASSERT(entry != NULL); |
| 186 ASSERT(NumOccupied() < num_entries); | 186 ASSERT(NumOccupied() < num_entries); |
| 187 NOT_IN_PRODUCT(intptr_t collisions = 0;) | 187 NOT_IN_PRODUCT(intptr_t collisions = 0;) |
| 188 uword hash = KeyTraits::Hash(key); | 188 uword hash = KeyTraits::Hash(key); |
| 189 intptr_t probe = hash % num_entries; | 189 intptr_t probe = hash % num_entries; |
| 190 intptr_t deleted = -1; | 190 intptr_t deleted = -1; |
| 191 // TODO(koda): Consider quadratic probing. | 191 // TODO(koda): Consider quadratic probing. |
| 192 while (true) { | 192 while (true) { |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 264 InternalSetKey(entry, Object::transition_sentinel()); | 264 InternalSetKey(entry, Object::transition_sentinel()); |
| 265 AdjustSmiValueAt(kOccupiedEntriesIndex, -1); | 265 AdjustSmiValueAt(kOccupiedEntriesIndex, -1); |
| 266 AdjustSmiValueAt(kDeletedEntriesIndex, 1); | 266 AdjustSmiValueAt(kDeletedEntriesIndex, 1); |
| 267 } | 267 } |
| 268 intptr_t NumEntries() const { | 268 intptr_t NumEntries() const { |
| 269 return (data_->Length() - kFirstKeyIndex) / kEntrySize; | 269 return (data_->Length() - kFirstKeyIndex) / kEntrySize; |
| 270 } | 270 } |
| 271 intptr_t NumUnused() const { | 271 intptr_t NumUnused() const { |
| 272 return NumEntries() - NumOccupied() - NumDeleted(); | 272 return NumEntries() - NumOccupied() - NumDeleted(); |
| 273 } | 273 } |
| 274 intptr_t NumOccupied() const { | 274 intptr_t NumOccupied() const { return GetSmiValueAt(kOccupiedEntriesIndex); } |
| 275 return GetSmiValueAt(kOccupiedEntriesIndex); | 275 intptr_t NumDeleted() const { return GetSmiValueAt(kDeletedEntriesIndex); } |
| 276 } | 276 Object& KeyHandle() const { return *key_handle_; } |
| 277 intptr_t NumDeleted() const { | 277 Smi& SmiHandle() const { return *smi_handle_; } |
| 278 return GetSmiValueAt(kDeletedEntriesIndex); | |
| 279 } | |
| 280 Object& KeyHandle() const { | |
| 281 return *key_handle_; | |
| 282 } | |
| 283 Smi& SmiHandle() const { | |
| 284 return *smi_handle_; | |
| 285 } | |
| 286 | 278 |
| 287 NOT_IN_PRODUCT( | 279 #if !defined(PRODUCT) |
| 288 intptr_t NumGrows() const { | 280 intptr_t NumGrows() const { return GetSmiValueAt(kNumGrowsIndex); } |
| 289 return GetSmiValueAt(kNumGrowsIndex); | |
| 290 } | |
| 291 intptr_t NumLT5Collisions() const { | 281 intptr_t NumLT5Collisions() const { |
| 292 return GetSmiValueAt(kNumLT5LookupsIndex); | 282 return GetSmiValueAt(kNumLT5LookupsIndex); |
| 293 } | 283 } |
| 294 intptr_t NumLT25Collisions() const { | 284 intptr_t NumLT25Collisions() const { |
| 295 return GetSmiValueAt(kNumLT25LookupsIndex); | 285 return GetSmiValueAt(kNumLT25LookupsIndex); |
| 296 } | 286 } |
| 297 intptr_t NumGT25Collisions() const { | 287 intptr_t NumGT25Collisions() const { |
| 298 return GetSmiValueAt(kNumGT25LookupsIndex); | 288 return GetSmiValueAt(kNumGT25LookupsIndex); |
| 299 } | 289 } |
| 300 void UpdateGrowth() const { | 290 void UpdateGrowth() const { AdjustSmiValueAt(kNumGrowsIndex, 1); } |
| 301 AdjustSmiValueAt(kNumGrowsIndex, 1); | |
| 302 } | |
| 303 void UpdateCollisions(intptr_t collisions) const { | 291 void UpdateCollisions(intptr_t collisions) const { |
| 304 if (data_->raw()->IsVMHeapObject()) { | 292 if (data_->raw()->IsVMHeapObject()) { |
| 305 return; | 293 return; |
| 306 } | 294 } |
| 307 if (collisions < 5) { | 295 if (collisions < 5) { |
| 308 AdjustSmiValueAt(kNumLT5LookupsIndex, 1); | 296 AdjustSmiValueAt(kNumLT5LookupsIndex, 1); |
| 309 } else if (collisions < 25) { | 297 } else if (collisions < 25) { |
| 310 AdjustSmiValueAt(kNumLT25LookupsIndex, 1); | 298 AdjustSmiValueAt(kNumLT25LookupsIndex, 1); |
| 311 } else { | 299 } else { |
| 312 AdjustSmiValueAt(kNumGT25LookupsIndex, 1); | 300 AdjustSmiValueAt(kNumGT25LookupsIndex, 1); |
| 313 } | 301 } |
| 314 } | 302 } |
| 315 void PrintStats() const { | 303 void PrintStats() const { |
| 316 if (!KeyTraits::ReportStats()) { | 304 if (!KeyTraits::ReportStats()) { |
| 317 return; | 305 return; |
| 318 } | 306 } |
| 307 // clang-format off |
| 319 OS::Print("Stats for %s table :\n" | 308 OS::Print("Stats for %s table :\n" |
| 320 " Size of table = %" Pd ",Number of Occupied entries = %" Pd "\n" | 309 " Size of table = %" Pd ",Number of Occupied entries = %" Pd "\n" |
| 321 " Number of Grows = %" Pd "\n" | 310 " Number of Grows = %" Pd "\n" |
| 322 " Number of look ups with < 5 collisions = %" Pd "\n" | 311 " Number of look ups with < 5 collisions = %" Pd "\n" |
| 323 " Number of look ups with < 25 collisions = %" Pd "\n" | 312 " Number of look ups with < 25 collisions = %" Pd "\n" |
| 324 " Number of look ups with > 25 collisions = %" Pd "\n", | 313 " Number of look ups with > 25 collisions = %" Pd "\n", |
| 325 KeyTraits::Name(), | 314 KeyTraits::Name(), |
| 326 NumEntries(), NumOccupied(), | 315 NumEntries(), NumOccupied(), |
| 327 NumGrows(), | 316 NumGrows(), |
| 328 NumLT5Collisions(), NumLT25Collisions(), NumGT25Collisions()); | 317 NumLT5Collisions(), NumLT25Collisions(), NumGT25Collisions()); |
| 318 // clang-format on |
| 329 } | 319 } |
| 330 ) // !PRODUCT | 320 #endif // !PRODUCT |
| 331 | 321 |
| 332 protected: | 322 protected: |
| 333 static const intptr_t kOccupiedEntriesIndex = 0; | 323 static const intptr_t kOccupiedEntriesIndex = 0; |
| 334 static const intptr_t kDeletedEntriesIndex = 1; | 324 static const intptr_t kDeletedEntriesIndex = 1; |
| 335 #if defined(PRODUCT) | 325 #if defined(PRODUCT) |
| 336 static const intptr_t kHeaderSize = kDeletedEntriesIndex + 1; | 326 static const intptr_t kHeaderSize = kDeletedEntriesIndex + 1; |
| 337 #else | 327 #else |
| 338 static const intptr_t kNumGrowsIndex = 2; | 328 static const intptr_t kNumGrowsIndex = 2; |
| 339 static const intptr_t kNumLT5LookupsIndex = 3; | 329 static const intptr_t kNumLT5LookupsIndex = 3; |
| 340 static const intptr_t kNumLT25LookupsIndex = 4; | 330 static const intptr_t kNumLT25LookupsIndex = 4; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 382 Smi* smi_handle_; | 372 Smi* smi_handle_; |
| 383 // Exactly one of these is non-NULL, depending on whether Release was called. | 373 // Exactly one of these is non-NULL, depending on whether Release was called. |
| 384 Array* data_; | 374 Array* data_; |
| 385 Array* released_data_; | 375 Array* released_data_; |
| 386 | 376 |
| 387 friend class HashTables; | 377 friend class HashTables; |
| 388 }; | 378 }; |
| 389 | 379 |
| 390 | 380 |
| 391 // Table with unspecified iteration order. No payload overhead or metadata. | 381 // Table with unspecified iteration order. No payload overhead or metadata. |
| 392 template<typename KeyTraits, intptr_t kUserPayloadSize> | 382 template <typename KeyTraits, intptr_t kUserPayloadSize> |
| 393 class UnorderedHashTable : public HashTable<KeyTraits, kUserPayloadSize, 0> { | 383 class UnorderedHashTable : public HashTable<KeyTraits, kUserPayloadSize, 0> { |
| 394 public: | 384 public: |
| 395 typedef HashTable<KeyTraits, kUserPayloadSize, 0> BaseTable; | 385 typedef HashTable<KeyTraits, kUserPayloadSize, 0> BaseTable; |
| 396 static const intptr_t kPayloadSize = kUserPayloadSize; | 386 static const intptr_t kPayloadSize = kUserPayloadSize; |
| 397 explicit UnorderedHashTable(RawArray* data) | 387 explicit UnorderedHashTable(RawArray* data) |
| 398 : BaseTable(Thread::Current()->zone(), data) {} | 388 : BaseTable(Thread::Current()->zone(), data) {} |
| 399 UnorderedHashTable(Zone* zone, RawArray* data) : BaseTable(zone, data) {} | 389 UnorderedHashTable(Zone* zone, RawArray* data) : BaseTable(zone, data) {} |
| 400 UnorderedHashTable(Object* key, Smi* value, Array* data) | 390 UnorderedHashTable(Object* key, Smi* value, Array* data) |
| 401 : BaseTable(key, value, data) {} | 391 : BaseTable(key, value, data) {} |
| 402 // Note: Does not check for concurrent modification. | 392 // Note: Does not check for concurrent modification. |
| 403 class Iterator { | 393 class Iterator { |
| 404 public: | 394 public: |
| 405 explicit Iterator(const UnorderedHashTable* table) | 395 explicit Iterator(const UnorderedHashTable* table) |
| 406 : table_(table), entry_(-1) {} | 396 : table_(table), entry_(-1) {} |
| 407 bool MoveNext() { | 397 bool MoveNext() { |
| 408 while (entry_ < (table_->NumEntries() - 1)) { | 398 while (entry_ < (table_->NumEntries() - 1)) { |
| 409 ++entry_; | 399 ++entry_; |
| 410 if (table_->IsOccupied(entry_)) { | 400 if (table_->IsOccupied(entry_)) { |
| 411 return true; | 401 return true; |
| 412 } | 402 } |
| 413 } | 403 } |
| 414 return false; | 404 return false; |
| 415 } | 405 } |
| 416 intptr_t Current() { | 406 intptr_t Current() { return entry_; } |
| 417 return entry_; | |
| 418 } | |
| 419 | 407 |
| 420 private: | 408 private: |
| 421 const UnorderedHashTable* table_; | 409 const UnorderedHashTable* table_; |
| 422 intptr_t entry_; | 410 intptr_t entry_; |
| 423 }; | 411 }; |
| 424 | 412 |
| 425 // No extra book-keeping needed for Initialize, InsertKey, DeleteEntry. | 413 // No extra book-keeping needed for Initialize, InsertKey, DeleteEntry. |
| 426 }; | 414 }; |
| 427 | 415 |
| 428 | 416 |
| 429 class HashTables : public AllStatic { | 417 class HashTables : public AllStatic { |
| 430 public: | 418 public: |
| 431 // Allocates and initializes a table. | 419 // Allocates and initializes a table. |
| 432 template<typename Table> | 420 template <typename Table> |
| 433 static RawArray* New(intptr_t initial_capacity, | 421 static RawArray* New(intptr_t initial_capacity, |
| 434 Heap::Space space = Heap::kNew) { | 422 Heap::Space space = Heap::kNew) { |
| 435 Table table(Thread::Current()->zone(), Array::New( | 423 Table table( |
| 436 Table::ArrayLengthForNumOccupied(initial_capacity), space)); | 424 Thread::Current()->zone(), |
| 425 Array::New(Table::ArrayLengthForNumOccupied(initial_capacity), space)); |
| 437 table.Initialize(); | 426 table.Initialize(); |
| 438 return table.Release().raw(); | 427 return table.Release().raw(); |
| 439 } | 428 } |
| 440 | 429 |
| 441 template<typename Table> | 430 template <typename Table> |
| 442 static RawArray* New(const Array& array) { | 431 static RawArray* New(const Array& array) { |
| 443 Table table(Thread::Current()->zone(), array.raw()); | 432 Table table(Thread::Current()->zone(), array.raw()); |
| 444 table.Initialize(); | 433 table.Initialize(); |
| 445 return table.Release().raw(); | 434 return table.Release().raw(); |
| 446 } | 435 } |
| 447 | 436 |
| 448 // Clears 'to' and inserts all elements from 'from', in iteration order. | 437 // Clears 'to' and inserts all elements from 'from', in iteration order. |
| 449 // The tables must have the same user payload size. | 438 // The tables must have the same user payload size. |
| 450 template<typename From, typename To> | 439 template <typename From, typename To> |
| 451 static void Copy(const From& from, const To& to) { | 440 static void Copy(const From& from, const To& to) { |
| 452 COMPILE_ASSERT(From::kPayloadSize == To::kPayloadSize); | 441 COMPILE_ASSERT(From::kPayloadSize == To::kPayloadSize); |
| 453 to.Initialize(); | 442 to.Initialize(); |
| 454 ASSERT(from.NumOccupied() < to.NumEntries()); | 443 ASSERT(from.NumOccupied() < to.NumEntries()); |
| 455 typename From::Iterator it(&from); | 444 typename From::Iterator it(&from); |
| 456 Object& obj = Object::Handle(); | 445 Object& obj = Object::Handle(); |
| 457 while (it.MoveNext()) { | 446 while (it.MoveNext()) { |
| 458 intptr_t from_entry = it.Current(); | 447 intptr_t from_entry = it.Current(); |
| 459 obj = from.GetKey(from_entry); | 448 obj = from.GetKey(from_entry); |
| 460 intptr_t to_entry = -1; | 449 intptr_t to_entry = -1; |
| 461 const Object& key = obj; | 450 const Object& key = obj; |
| 462 bool present = to.FindKeyOrDeletedOrUnused(key, &to_entry); | 451 bool present = to.FindKeyOrDeletedOrUnused(key, &to_entry); |
| 463 ASSERT(!present); | 452 ASSERT(!present); |
| 464 to.InsertKey(to_entry, obj); | 453 to.InsertKey(to_entry, obj); |
| 465 for (intptr_t i = 0; i < From::kPayloadSize; ++i) { | 454 for (intptr_t i = 0; i < From::kPayloadSize; ++i) { |
| 466 obj = from.GetPayload(from_entry, i); | 455 obj = from.GetPayload(from_entry, i); |
| 467 to.UpdatePayload(to_entry, i, obj); | 456 to.UpdatePayload(to_entry, i, obj); |
| 468 } | 457 } |
| 469 } | 458 } |
| 470 } | 459 } |
| 471 | 460 |
| 472 template<typename Table> | 461 template <typename Table> |
| 473 static void EnsureLoadFactor(double low, double high, const Table& table) { | 462 static void EnsureLoadFactor(double low, double high, const Table& table) { |
| 474 double current = (1 + table.NumOccupied() + table.NumDeleted()) / | 463 double current = (1 + table.NumOccupied() + table.NumDeleted()) / |
| 475 static_cast<double>(table.NumEntries()); | 464 static_cast<double>(table.NumEntries()); |
| 476 if (low <= current && current < high) { | 465 if (low <= current && current < high) { |
| 477 return; | 466 return; |
| 478 } | 467 } |
| 479 double target = (low + high) / 2.0; | 468 double target = (low + high) / 2.0; |
| 480 intptr_t new_capacity = (1 + table.NumOccupied()) / target; | 469 intptr_t new_capacity = (1 + table.NumOccupied()) / target; |
| 481 Table new_table(New<Table>( | 470 Table new_table(New<Table>(new_capacity, |
| 482 new_capacity, | 471 table.data_->IsOld() ? Heap::kOld : Heap::kNew)); |
| 483 table.data_->IsOld() ? Heap::kOld : Heap::kNew)); | |
| 484 Copy(table, new_table); | 472 Copy(table, new_table); |
| 485 *table.data_ = new_table.Release().raw(); | 473 *table.data_ = new_table.Release().raw(); |
| 486 NOT_IN_PRODUCT(table.UpdateGrowth(); table.PrintStats();) | 474 NOT_IN_PRODUCT(table.UpdateGrowth(); table.PrintStats();) |
| 487 } | 475 } |
| 488 | 476 |
| 489 // Serializes a table by concatenating its entries as an array. | 477 // Serializes a table by concatenating its entries as an array. |
| 490 template<typename Table> | 478 template <typename Table> |
| 491 static RawArray* ToArray(const Table& table, bool include_payload) { | 479 static RawArray* ToArray(const Table& table, bool include_payload) { |
| 492 const intptr_t entry_size = include_payload ? (1 + Table::kPayloadSize) : 1; | 480 const intptr_t entry_size = include_payload ? (1 + Table::kPayloadSize) : 1; |
| 493 Array& result = Array::Handle(Array::New(table.NumOccupied() * entry_size)); | 481 Array& result = Array::Handle(Array::New(table.NumOccupied() * entry_size)); |
| 494 typename Table::Iterator it(&table); | 482 typename Table::Iterator it(&table); |
| 495 Object& obj = Object::Handle(); | 483 Object& obj = Object::Handle(); |
| 496 intptr_t result_index = 0; | 484 intptr_t result_index = 0; |
| 497 while (it.MoveNext()) { | 485 while (it.MoveNext()) { |
| 498 intptr_t entry = it.Current(); | 486 intptr_t entry = it.Current(); |
| 499 obj = table.GetKey(entry); | 487 obj = table.GetKey(entry); |
| 500 result.SetAt(result_index++, obj); | 488 result.SetAt(result_index++, obj); |
| 501 if (include_payload) { | 489 if (include_payload) { |
| 502 for (intptr_t i = 0; i < Table::kPayloadSize; ++i) { | 490 for (intptr_t i = 0; i < Table::kPayloadSize; ++i) { |
| 503 obj = table.GetPayload(entry, i); | 491 obj = table.GetPayload(entry, i); |
| 504 result.SetAt(result_index++, obj); | 492 result.SetAt(result_index++, obj); |
| 505 } | 493 } |
| 506 } | 494 } |
| 507 } | 495 } |
| 508 return result.raw(); | 496 return result.raw(); |
| 509 } | 497 } |
| 510 }; | 498 }; |
| 511 | 499 |
| 512 | 500 |
| 513 template<typename BaseIterTable> | 501 template <typename BaseIterTable> |
| 514 class HashMap : public BaseIterTable { | 502 class HashMap : public BaseIterTable { |
| 515 public: | 503 public: |
| 516 explicit HashMap(RawArray* data) | 504 explicit HashMap(RawArray* data) |
| 517 : BaseIterTable(Thread::Current()->zone(), data) {} | 505 : BaseIterTable(Thread::Current()->zone(), data) {} |
| 518 HashMap(Zone* zone, RawArray* data) : BaseIterTable(zone, data) {} | 506 HashMap(Zone* zone, RawArray* data) : BaseIterTable(zone, data) {} |
| 519 HashMap(Object* key, Smi* value, Array* data) | 507 HashMap(Object* key, Smi* value, Array* data) |
| 520 : BaseIterTable(key, value, data) {} | 508 : BaseIterTable(key, value, data) {} |
| 521 template<typename Key> | 509 template <typename Key> |
| 522 RawObject* GetOrNull(const Key& key, bool* present = NULL) const { | 510 RawObject* GetOrNull(const Key& key, bool* present = NULL) const { |
| 523 intptr_t entry = BaseIterTable::FindKey(key); | 511 intptr_t entry = BaseIterTable::FindKey(key); |
| 524 if (present != NULL) { | 512 if (present != NULL) { |
| 525 *present = (entry != -1); | 513 *present = (entry != -1); |
| 526 } | 514 } |
| 527 return (entry == -1) ? Object::null() : BaseIterTable::GetPayload(entry, 0); | 515 return (entry == -1) ? Object::null() : BaseIterTable::GetPayload(entry, 0); |
| 528 } | 516 } |
| 529 bool UpdateOrInsert(const Object& key, const Object& value) const { | 517 bool UpdateOrInsert(const Object& key, const Object& value) const { |
| 530 EnsureCapacity(); | 518 EnsureCapacity(); |
| 531 intptr_t entry = -1; | 519 intptr_t entry = -1; |
| 532 bool present = BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry); | 520 bool present = BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry); |
| 533 if (!present) { | 521 if (!present) { |
| 534 BaseIterTable::InsertKey(entry, key); | 522 BaseIterTable::InsertKey(entry, key); |
| 535 } | 523 } |
| 536 BaseIterTable::UpdatePayload(entry, 0, value); | 524 BaseIterTable::UpdatePayload(entry, 0, value); |
| 537 return present; | 525 return present; |
| 538 } | 526 } |
| 539 // Update the value of an existing key. Note that 'key' need not be an Object. | 527 // Update the value of an existing key. Note that 'key' need not be an Object. |
| 540 template<typename Key> | 528 template <typename Key> |
| 541 void UpdateValue(const Key& key, const Object& value) const { | 529 void UpdateValue(const Key& key, const Object& value) const { |
| 542 intptr_t entry = BaseIterTable::FindKey(key); | 530 intptr_t entry = BaseIterTable::FindKey(key); |
| 543 ASSERT(entry != -1); | 531 ASSERT(entry != -1); |
| 544 BaseIterTable::UpdatePayload(entry, 0, value); | 532 BaseIterTable::UpdatePayload(entry, 0, value); |
| 545 } | 533 } |
| 546 // If 'key' is not present, maps it to 'value_if_absent'. Returns the final | 534 // If 'key' is not present, maps it to 'value_if_absent'. Returns the final |
| 547 // value in the map. | 535 // value in the map. |
| 548 RawObject* InsertOrGetValue(const Object& key, | 536 RawObject* InsertOrGetValue(const Object& key, |
| 549 const Object& value_if_absent) const { | 537 const Object& value_if_absent) const { |
| 550 EnsureCapacity(); | 538 EnsureCapacity(); |
| 551 intptr_t entry = -1; | 539 intptr_t entry = -1; |
| 552 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { | 540 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
| 553 BaseIterTable::InsertKey(entry, key); | 541 BaseIterTable::InsertKey(entry, key); |
| 554 BaseIterTable::UpdatePayload(entry, 0, value_if_absent); | 542 BaseIterTable::UpdatePayload(entry, 0, value_if_absent); |
| 555 return value_if_absent.raw(); | 543 return value_if_absent.raw(); |
| 556 } else { | 544 } else { |
| 557 return BaseIterTable::GetPayload(entry, 0); | 545 return BaseIterTable::GetPayload(entry, 0); |
| 558 } | 546 } |
| 559 } | 547 } |
| 560 // Like InsertOrGetValue, but calls NewKey to allocate a key object if needed. | 548 // Like InsertOrGetValue, but calls NewKey to allocate a key object if needed. |
| 561 template<typename Key> | 549 template <typename Key> |
| 562 RawObject* InsertNewOrGetValue(const Key& key, | 550 RawObject* InsertNewOrGetValue(const Key& key, |
| 563 const Object& value_if_absent) const { | 551 const Object& value_if_absent) const { |
| 564 EnsureCapacity(); | 552 EnsureCapacity(); |
| 565 intptr_t entry = -1; | 553 intptr_t entry = -1; |
| 566 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { | 554 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
| 567 BaseIterTable::KeyHandle() = | 555 BaseIterTable::KeyHandle() = |
| 568 BaseIterTable::BaseTable::Traits::NewKey(key); | 556 BaseIterTable::BaseTable::Traits::NewKey(key); |
| 569 BaseIterTable::InsertKey(entry, BaseIterTable::KeyHandle()); | 557 BaseIterTable::InsertKey(entry, BaseIterTable::KeyHandle()); |
| 570 BaseIterTable::UpdatePayload(entry, 0, value_if_absent); | 558 BaseIterTable::UpdatePayload(entry, 0, value_if_absent); |
| 571 return value_if_absent.raw(); | 559 return value_if_absent.raw(); |
| 572 } else { | 560 } else { |
| 573 return BaseIterTable::GetPayload(entry, 0); | 561 return BaseIterTable::GetPayload(entry, 0); |
| 574 } | 562 } |
| 575 } | 563 } |
| 576 | 564 |
| 577 template<typename Key> | 565 template <typename Key> |
| 578 bool Remove(const Key& key) const { | 566 bool Remove(const Key& key) const { |
| 579 intptr_t entry = BaseIterTable::FindKey(key); | 567 intptr_t entry = BaseIterTable::FindKey(key); |
| 580 if (entry == -1) { | 568 if (entry == -1) { |
| 581 return false; | 569 return false; |
| 582 } else { | 570 } else { |
| 583 BaseIterTable::DeleteEntry(entry); | 571 BaseIterTable::DeleteEntry(entry); |
| 584 return true; | 572 return true; |
| 585 } | 573 } |
| 586 } | 574 } |
| 587 | 575 |
| 588 void Clear() const { | 576 void Clear() const { BaseIterTable::Initialize(); } |
| 589 BaseIterTable::Initialize(); | |
| 590 } | |
| 591 | 577 |
| 592 protected: | 578 protected: |
| 593 void EnsureCapacity() const { | 579 void EnsureCapacity() const { |
| 594 static const double kMaxLoadFactor = 0.75; | 580 static const double kMaxLoadFactor = 0.75; |
| 595 // We currently never shrink. | 581 // We currently never shrink. |
| 596 HashTables::EnsureLoadFactor(0.0, kMaxLoadFactor, *this); | 582 HashTables::EnsureLoadFactor(0.0, kMaxLoadFactor, *this); |
| 597 } | 583 } |
| 598 }; | 584 }; |
| 599 | 585 |
| 600 | 586 |
| 601 template<typename KeyTraits> | 587 template <typename KeyTraits> |
| 602 class UnorderedHashMap : public HashMap<UnorderedHashTable<KeyTraits, 1> > { | 588 class UnorderedHashMap : public HashMap<UnorderedHashTable<KeyTraits, 1> > { |
| 603 public: | 589 public: |
| 604 typedef HashMap<UnorderedHashTable<KeyTraits, 1> > BaseMap; | 590 typedef HashMap<UnorderedHashTable<KeyTraits, 1> > BaseMap; |
| 605 explicit UnorderedHashMap(RawArray* data) | 591 explicit UnorderedHashMap(RawArray* data) |
| 606 : BaseMap(Thread::Current()->zone(), data) {} | 592 : BaseMap(Thread::Current()->zone(), data) {} |
| 607 UnorderedHashMap(Zone* zone, RawArray* data) : BaseMap(zone, data) {} | 593 UnorderedHashMap(Zone* zone, RawArray* data) : BaseMap(zone, data) {} |
| 608 UnorderedHashMap(Object* key, Smi* value, Array* data) | 594 UnorderedHashMap(Object* key, Smi* value, Array* data) |
| 609 : BaseMap(key, value, data) {} | 595 : BaseMap(key, value, data) {} |
| 610 }; | 596 }; |
| 611 | 597 |
| 612 | 598 |
| 613 template<typename BaseIterTable> | 599 template <typename BaseIterTable> |
| 614 class HashSet : public BaseIterTable { | 600 class HashSet : public BaseIterTable { |
| 615 public: | 601 public: |
| 616 explicit HashSet(RawArray* data) | 602 explicit HashSet(RawArray* data) |
| 617 : BaseIterTable(Thread::Current()->zone(), data) {} | 603 : BaseIterTable(Thread::Current()->zone(), data) {} |
| 618 HashSet(Zone* zone, RawArray* data) : BaseIterTable(zone, data) {} | 604 HashSet(Zone* zone, RawArray* data) : BaseIterTable(zone, data) {} |
| 619 HashSet(Object* key, Smi* value, Array* data) | 605 HashSet(Object* key, Smi* value, Array* data) |
| 620 : BaseIterTable(key, value, data) {} | 606 : BaseIterTable(key, value, data) {} |
| 621 bool Insert(const Object& key) { | 607 bool Insert(const Object& key) { |
| 622 EnsureCapacity(); | 608 EnsureCapacity(); |
| 623 intptr_t entry = -1; | 609 intptr_t entry = -1; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 635 intptr_t entry = -1; | 621 intptr_t entry = -1; |
| 636 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { | 622 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
| 637 BaseIterTable::InsertKey(entry, key); | 623 BaseIterTable::InsertKey(entry, key); |
| 638 return key.raw(); | 624 return key.raw(); |
| 639 } else { | 625 } else { |
| 640 return BaseIterTable::GetKey(entry); | 626 return BaseIterTable::GetKey(entry); |
| 641 } | 627 } |
| 642 } | 628 } |
| 643 | 629 |
| 644 // Like InsertOrGet, but calls NewKey to allocate a key object if needed. | 630 // Like InsertOrGet, but calls NewKey to allocate a key object if needed. |
| 645 template<typename Key> | 631 template <typename Key> |
| 646 RawObject* InsertNewOrGet(const Key& key) const { | 632 RawObject* InsertNewOrGet(const Key& key) const { |
| 647 EnsureCapacity(); | 633 EnsureCapacity(); |
| 648 intptr_t entry = -1; | 634 intptr_t entry = -1; |
| 649 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { | 635 if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
| 650 BaseIterTable::KeyHandle() = | 636 BaseIterTable::KeyHandle() = |
| 651 BaseIterTable::BaseTable::Traits::NewKey(key); | 637 BaseIterTable::BaseTable::Traits::NewKey(key); |
| 652 BaseIterTable::InsertKey(entry, BaseIterTable::KeyHandle()); | 638 BaseIterTable::InsertKey(entry, BaseIterTable::KeyHandle()); |
| 653 return BaseIterTable::KeyHandle().raw(); | 639 return BaseIterTable::KeyHandle().raw(); |
| 654 } else { | 640 } else { |
| 655 return BaseIterTable::GetKey(entry); | 641 return BaseIterTable::GetKey(entry); |
| 656 } | 642 } |
| 657 } | 643 } |
| 658 | 644 |
| 659 template<typename Key> | 645 template <typename Key> |
| 660 RawObject* GetOrNull(const Key& key, bool* present = NULL) const { | 646 RawObject* GetOrNull(const Key& key, bool* present = NULL) const { |
| 661 intptr_t entry = BaseIterTable::FindKey(key); | 647 intptr_t entry = BaseIterTable::FindKey(key); |
| 662 if (present != NULL) { | 648 if (present != NULL) { |
| 663 *present = (entry != -1); | 649 *present = (entry != -1); |
| 664 } | 650 } |
| 665 return (entry == -1) ? Object::null() : BaseIterTable::GetKey(entry); | 651 return (entry == -1) ? Object::null() : BaseIterTable::GetKey(entry); |
| 666 } | 652 } |
| 667 | 653 |
| 668 template<typename Key> | 654 template <typename Key> |
| 669 bool Remove(const Key& key) const { | 655 bool Remove(const Key& key) const { |
| 670 intptr_t entry = BaseIterTable::FindKey(key); | 656 intptr_t entry = BaseIterTable::FindKey(key); |
| 671 if (entry == -1) { | 657 if (entry == -1) { |
| 672 return false; | 658 return false; |
| 673 } else { | 659 } else { |
| 674 BaseIterTable::DeleteEntry(entry); | 660 BaseIterTable::DeleteEntry(entry); |
| 675 return true; | 661 return true; |
| 676 } | 662 } |
| 677 } | 663 } |
| 678 | 664 |
| 679 void Clear() const { | 665 void Clear() const { BaseIterTable::Initialize(); } |
| 680 BaseIterTable::Initialize(); | |
| 681 } | |
| 682 | 666 |
| 683 protected: | 667 protected: |
| 684 void EnsureCapacity() const { | 668 void EnsureCapacity() const { |
| 685 static const double kMaxLoadFactor = 0.75; | 669 static const double kMaxLoadFactor = 0.75; |
| 686 // We currently never shrink. | 670 // We currently never shrink. |
| 687 HashTables::EnsureLoadFactor(0.0, kMaxLoadFactor, *this); | 671 HashTables::EnsureLoadFactor(0.0, kMaxLoadFactor, *this); |
| 688 } | 672 } |
| 689 }; | 673 }; |
| 690 | 674 |
| 691 | 675 |
| 692 template<typename KeyTraits> | 676 template <typename KeyTraits> |
| 693 class UnorderedHashSet : public HashSet<UnorderedHashTable<KeyTraits, 0> > { | 677 class UnorderedHashSet : public HashSet<UnorderedHashTable<KeyTraits, 0> > { |
| 694 public: | 678 public: |
| 695 typedef HashSet<UnorderedHashTable<KeyTraits, 0> > BaseSet; | 679 typedef HashSet<UnorderedHashTable<KeyTraits, 0> > BaseSet; |
| 696 explicit UnorderedHashSet(RawArray* data) | 680 explicit UnorderedHashSet(RawArray* data) |
| 697 : BaseSet(Thread::Current()->zone(), data) { | 681 : BaseSet(Thread::Current()->zone(), data) { |
| 698 ASSERT(data != Array::null()); | 682 ASSERT(data != Array::null()); |
| 699 } | 683 } |
| 700 UnorderedHashSet(Zone* zone, RawArray* data) : BaseSet(zone, data) {} | 684 UnorderedHashSet(Zone* zone, RawArray* data) : BaseSet(zone, data) {} |
| 701 UnorderedHashSet(Object* key, Smi* value, Array* data) | 685 UnorderedHashSet(Object* key, Smi* value, Array* data) |
| 702 : BaseSet(key, value, data) {} | 686 : BaseSet(key, value, data) {} |
| 703 }; | 687 }; |
| 704 | 688 |
| 705 } // namespace dart | 689 } // namespace dart |
| 706 | 690 |
| 707 #endif // RUNTIME_VM_HASH_TABLE_H_ | 691 #endif // RUNTIME_VM_HASH_TABLE_H_ |
| OLD | NEW |