// 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 VM_WEAK_TABLE_H_ #define VM_WEAK_TABLE_H_ #include "vm/globals.h" #include "platform/assert.h" #include "vm/raw_object.h" namespace dart { class WeakTable { public: WeakTable() : size_(kMinSize), used_(0), count_(0) { ASSERT(Utils::IsPowerOfTwo(size_)); data_ = reinterpret_cast(calloc(size_, kEntrySize * kWordSize)); } 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(calloc(size_, kEntrySize * kWordSize)); } ~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_; } bool IsValidEntryAt(intptr_t i) const { ASSERT(((ValueAt(i) == 0) && ((ObjectAt(i) == NULL) || (data_[ObjectIndex(i)] == kDeletedEntry))) || ((ValueAt(i) != 0) && (ObjectAt(i) != NULL) && (data_[ObjectIndex(i)] != kDeletedEntry))); return (data_[ValueIndex(i)] != 0); } void InvalidateAt(intptr_t i) { ASSERT(IsValidEntryAt(i)); SetValueAt(i, 0); } RawObject* ObjectAt(intptr_t i) const { ASSERT(i >= 0); ASSERT(i < size()); return reinterpret_cast(data_[ObjectIndex(i)]); } intptr_t ValueAt(intptr_t i) const { ASSERT(i >= 0); ASSERT(i < size()); return data_[ValueIndex(i)]; } void SetValue(RawObject* key, intptr_t val); intptr_t GetValue(RawObject* key) const { intptr_t mask = size() - 1; intptr_t idx = Hash(key) & mask; RawObject* obj = ObjectAt(idx); while (obj != NULL) { if (obj == key) { return ValueAt(idx); } idx = (idx + 1) & mask; obj = ObjectAt(idx); } ASSERT(ValueAt(idx) == 0); return 0; } private: enum { kObjectOffset = 0, kValueOffset, kEntrySize, }; static const intptr_t kDeletedEntry = 1; // Equivalent to a tagged NULL. 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; } void SetObjectAt(intptr_t i, RawObject* key) { ASSERT(i >= 0); ASSERT(i < size()); data_[ObjectIndex(i)] = reinterpret_cast(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 intptr_t Hash(RawObject* key) { return reinterpret_cast(key) >> kObjectAlignmentLog2; } // 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 // VM_WEAK_TABLE_H_