| // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #ifndef RUNTIME_VM_HEAP_WEAK_TABLE_H_ | 
 | #define RUNTIME_VM_HEAP_WEAK_TABLE_H_ | 
 |  | 
 | #include "vm/globals.h" | 
 |  | 
 | #include "platform/assert.h" | 
 | #include "vm/lockers.h" | 
 | #include "vm/raw_object.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | class WeakTable { | 
 |  public: | 
 |   static constexpr intptr_t kNoValue = 0; | 
 |  | 
 |   WeakTable() : WeakTable(kMinSize) {} | 
 |   explicit WeakTable(intptr_t size) : used_(0), count_(0) { | 
 |     ASSERT(size >= 0); | 
 |     ASSERT(Utils::IsPowerOfTwo(kMinSize)); | 
 |     if (size < kMinSize) { | 
 |       size = kMinSize; | 
 |     } | 
 |     // Get a max size that avoids overflows. | 
 |     const intptr_t kMaxSize = | 
 |         (kIntptrOne << (kBitsPerWord - 2)) / (kEntrySize * kWordSize); | 
 |     ASSERT(Utils::IsPowerOfTwo(kMaxSize)); | 
 |     if (size > kMaxSize) { | 
 |       size = kMaxSize; | 
 |     } | 
 |     size_ = size; | 
 |     ASSERT(Utils::IsPowerOfTwo(size_)); | 
 |     data_ = reinterpret_cast<intptr_t*>(malloc(size_ * kEntrySize * kWordSize)); | 
 |     for (intptr_t i = 0; i < size_; i++) { | 
 |       data_[ObjectIndex(i)] = kNoEntry; | 
 |       data_[ValueIndex(i)] = kNoValue; | 
 |     } | 
 |   } | 
 |  | 
 |   ~WeakTable() { free(data_); } | 
 |  | 
 |   static WeakTable* NewFrom(WeakTable* original) { | 
 |     return new WeakTable(SizeFor(original->count(), original->size())); | 
 |   } | 
 |  | 
 |   intptr_t size() const { return size_; } | 
 |   intptr_t used() const { return used_; } | 
 |   intptr_t count() const { return count_; } | 
 |  | 
 |   // The following methods can be called concurrently and are guarded by a lock. | 
 |  | 
 |   intptr_t GetValue(ObjectPtr key) { | 
 |     MutexLocker ml(&mutex_); | 
 |     return GetValueExclusive(key); | 
 |   } | 
 |  | 
 |   void SetValue(ObjectPtr key, intptr_t val) { | 
 |     MutexLocker ml(&mutex_); | 
 |     return SetValueExclusive(key, val); | 
 |   } | 
 |  | 
 |   intptr_t SetValueIfNonExistent(ObjectPtr key, intptr_t val) { | 
 |     MutexLocker ml(&mutex_); | 
 |     const auto old_value = GetValueExclusive(key); | 
 |     if (old_value == kNoValue) { | 
 |       SetValueExclusive(key, val); | 
 |       return val; | 
 |     } | 
 |     return old_value; | 
 |   } | 
 |  | 
 |   // The following "exclusive" methods must only be called from call sites | 
 |   // which are known to have exclusive access to the weak table. | 
 |   // | 
 |   // This is mostly limited to GC related code (e.g. scavenger, marker, ...) | 
 |  | 
 |   bool IsValidEntryAtExclusive(intptr_t i) const { | 
 |     ASSERT((ValueAtExclusive(i) == 0 && | 
 |             (data_[ObjectIndex(i)] == kNoEntry || | 
 |              data_[ObjectIndex(i)] == kDeletedEntry)) || | 
 |            (ValueAtExclusive(i) != 0 && data_[ObjectIndex(i)] != kNoEntry && | 
 |             data_[ObjectIndex(i)] != kDeletedEntry)); | 
 |     return (data_[ValueIndex(i)] != 0); | 
 |   } | 
 |  | 
 |   void InvalidateAtExclusive(intptr_t i) { | 
 |     ASSERT(IsValidEntryAtExclusive(i)); | 
 |     SetValueAt(i, 0); | 
 |   } | 
 |  | 
 |   ObjectPtr ObjectAtExclusive(intptr_t i) const { | 
 |     ASSERT(i >= 0); | 
 |     ASSERT(i < size()); | 
 |     return static_cast<ObjectPtr>(data_[ObjectIndex(i)]); | 
 |   } | 
 |  | 
 |   intptr_t ValueAtExclusive(intptr_t i) const { | 
 |     ASSERT(i >= 0); | 
 |     ASSERT(i < size()); | 
 |     return data_[ValueIndex(i)]; | 
 |   } | 
 |  | 
 |   void SetValueExclusive(ObjectPtr key, intptr_t val); | 
 |   bool MarkValueExclusive(ObjectPtr key, intptr_t val); | 
 |  | 
 |   intptr_t GetValueExclusive(ObjectPtr key) const { | 
 |     intptr_t mask = size() - 1; | 
 |     intptr_t idx = Hash(key) & mask; | 
 |     ObjectPtr obj = ObjectAtExclusive(idx); | 
 |     while (obj != static_cast<ObjectPtr>(kNoEntry)) { | 
 |       if (obj == key) { | 
 |         return ValueAtExclusive(idx); | 
 |       } | 
 |       idx = (idx + 1) & mask; | 
 |       obj = ObjectAtExclusive(idx); | 
 |     } | 
 |     ASSERT(ValueAtExclusive(idx) == 0); | 
 |     return kNoValue; | 
 |   } | 
 |  | 
 |   // Removes and returns the value associated with |key|. Returns 0 if there is | 
 |   // no value associated with |key|. | 
 |   intptr_t RemoveValueExclusive(ObjectPtr key) { | 
 |     intptr_t mask = size() - 1; | 
 |     intptr_t idx = Hash(key) & mask; | 
 |     ObjectPtr obj = ObjectAtExclusive(idx); | 
 |     while (obj != static_cast<ObjectPtr>(kNoEntry)) { | 
 |       if (obj == key) { | 
 |         intptr_t result = ValueAtExclusive(idx); | 
 |         InvalidateAtExclusive(idx); | 
 |         return result; | 
 |       } | 
 |       idx = (idx + 1) & mask; | 
 |       obj = ObjectAtExclusive(idx); | 
 |     } | 
 |     ASSERT(ValueAtExclusive(idx) == 0); | 
 |     return kNoValue; | 
 |   } | 
 |  | 
 |   void Forward(ObjectPointerVisitor* visitor); | 
 |  | 
 |   void Reset(); | 
 |  | 
 |  private: | 
 |   enum { | 
 |     kObjectOffset = 0, | 
 |     kValueOffset, | 
 |     kEntrySize, | 
 |   }; | 
 |  | 
 |   static const intptr_t kNoEntry = 1;       // Not a valid OOP. | 
 |   static const intptr_t kDeletedEntry = 3;  // Not a valid OOP. | 
 |   static const intptr_t kMinSize = 8; | 
 |  | 
 |   static intptr_t SizeFor(intptr_t count, intptr_t size); | 
 |   static intptr_t LimitFor(intptr_t size) { | 
 |     // Maintain a maximum of 75% fill rate. | 
 |     return 3 * (size / 4); | 
 |   } | 
 |   intptr_t limit() const { return LimitFor(size()); } | 
 |  | 
 |   intptr_t index(intptr_t i) const { return i * kEntrySize; } | 
 |  | 
 |   void set_used(intptr_t val) { | 
 |     ASSERT(val <= limit()); | 
 |     used_ = val; | 
 |   } | 
 |  | 
 |   void set_count(intptr_t val) { | 
 |     ASSERT(val <= limit()); | 
 |     ASSERT(val <= used()); | 
 |     count_ = val; | 
 |   } | 
 |  | 
 |   intptr_t ObjectIndex(intptr_t i) const { return index(i) + kObjectOffset; } | 
 |  | 
 |   intptr_t ValueIndex(intptr_t i) const { return index(i) + kValueOffset; } | 
 |  | 
 |   ObjectPtr* ObjectPointerAt(intptr_t i) const { | 
 |     ASSERT(i >= 0); | 
 |     ASSERT(i < size()); | 
 |     return reinterpret_cast<ObjectPtr*>(&data_[ObjectIndex(i)]); | 
 |   } | 
 |  | 
 |   void SetObjectAt(intptr_t i, ObjectPtr key) { | 
 |     ASSERT(i >= 0); | 
 |     ASSERT(i < size()); | 
 |     data_[ObjectIndex(i)] = static_cast<intptr_t>(key); | 
 |   } | 
 |  | 
 |   void SetValueAt(intptr_t i, intptr_t val) { | 
 |     ASSERT(i >= 0); | 
 |     ASSERT(i < size()); | 
 |     // Setting a value of 0 is equivalent to invalidating the entry. | 
 |     if (val == 0) { | 
 |       data_[ObjectIndex(i)] = kDeletedEntry; | 
 |       set_count(count() - 1); | 
 |     } | 
 |     data_[ValueIndex(i)] = val; | 
 |   } | 
 |  | 
 |   void Rehash(); | 
 |  | 
 |   static uword Hash(ObjectPtr key) { | 
 |     return (static_cast<uword>(key) * 92821) ^ (static_cast<uword>(key) >> 8); | 
 |   } | 
 |  | 
 |   Mutex mutex_; | 
 |  | 
 |   // data_ contains size_ tuples of key/value. | 
 |   intptr_t* data_; | 
 |   // size_ keeps the number of entries in data_. used_ maintains the number of | 
 |   // non-NULL entries and will trigger rehashing if needed. count_ stores the | 
 |   // number valid entries, and will determine the size_ after rehashing. | 
 |   intptr_t size_; | 
 |   intptr_t used_; | 
 |   intptr_t count_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(WeakTable); | 
 | }; | 
 |  | 
 | }  // namespace dart | 
 |  | 
 | #endif  // RUNTIME_VM_HEAP_WEAK_TABLE_H_ |