- Add a WeakTable to the VM. This is used to remember the

native peers registered through the Dart C API as well
  as assigning identity hashcodes to objects when needed.
- Use the hashcode to lookup entries in the Expando.

R=asiva@google.com

Review URL: https://codereview.chromium.org//17992002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@24563 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
iposva@google.com 2013-06-28 00:01:47 +00:00
parent c326893f6a
commit 9c9989d2f6
15 changed files with 491 additions and 158 deletions

View file

@ -3,61 +3,135 @@
// BSD-style license that can be found in the LICENSE file.
patch class Expando<T> {
/* patch */ Expando([this.name]) : _data = new List();
/* patch */ Expando([this.name])
: _data = new List(_minSize),
_used = 0;
static const _minSize = 8;
static final _deletedEntry = new _WeakProperty(null, null);
/* patch */ T operator[](Object object) {
_checkType(object);
var doCompact = false;
var result = null;
for (int i = 0; i < _data.length; ++i) {
var key = _data[i].key;
if (identical(key, object)) {
result = _data[i].value;
break;
}
if (key == null) {
doCompact = true;
_data[i] = null;
var mask = _size - 1;
var idx = object.hashCode & mask;
var wp = _data[idx];
while (wp != null) {
if (identical(wp.key, object)) {
return wp.value;
} else if (wp.key == null) {
// This entry has been cleared by the GC.
_data[idx] = _deletedEntry;
}
idx = (idx + 1) & mask;
wp = _data[idx];
}
if (doCompact) {
_data = _data.where((e) => (e != null)).toList();
}
return result;
return null;
}
/* patch */ void operator[]=(Object object, T value) {
_checkType(object);
var doCompact = false;
int i = 0;
for (; i < _data.length; ++i) {
var key = _data[i].key;
if (identical(key, object)) {
break;
var mask = _size - 1;
var idx = object.hashCode & mask;
var empty_idx = -1;
var wp = _data[idx];
while (wp != null) {
if (identical(wp.key, object)) {
if (value != null) {
// Update the associated value.
wp.value = value;
} else {
// Mark the entry as deleted.
_data[idx] = _deletedEntry;
}
return;
} else if ((empty_idx < 0) && identical(wp, _deletedEntry)) {
empty_idx = idx; // Insert at this location if not found.
} else if (wp.key == null) {
// This entry has been cleared by the GC.
_data[idx] = _deletedEntry;
if (empty_idx < 0) {
empty_idx = idx; // Insert at this location if not found.
}
}
if (key == null) {
doCompact = true;
_data[i] = null;
idx = (idx + 1) & mask;
wp = _data[idx];
}
if (value == null) {
// Not entering a null value. We just needed to make sure to clear an
// existing value if it existed.
return;
}
if (empty_idx >= 0) {
// We will be reusing the empty slot below.
_used--;
idx = empty_idx;
}
if (_used < _limit) {
_data[idx] = new _WeakProperty(object, value);
_used++;
return;
}
// Grow/reallocate if too many slots have been used.
_rehash();
this[object] = value; // Recursively add the value.
}
_rehash() {
// Determine the population count of the map to allocate an appropriately
// sized map below.
var count = 0;
var old_data = _data;
var len = old_data.length;
for (var i = 0; i < len; i++) {
var entry = old_data[i];
if ((entry != null) && (entry.key != null)) {
// Only count non-cleared entries.
count++;
}
}
if (i != _data.length && value == null) {
doCompact = true;
_data[i] = null;
} else if (i != _data.length) {
_data[i].value = value;
} else {
_data.add(new _WeakProperty(object, value));
var new_size = _size;
if (count <= (new_size >> 2)) {
new_size = new_size >> 1;
} else if (count > (new_size >> 1)) {
new_size = new_size << 1;
}
if (doCompact) {
_data = _data.where((e) => (e != null)).toList();
new_size = (new_size < _minSize) ? _minSize : new_size;
// Reset the mappings to empty so that we can just add the existing
// valid entries.
_data = new List(new_size);
_used = 0;
for (var i = 0; i < old_data.length; i++) {
var entry = old_data[i];
if ((entry != null) && (entry.key != null)) {
this[entry.key] = entry.value;
}
}
}
static _checkType(object) {
if (object == null || object is bool || object is num || object is String) {
if ((object == null) ||
(object is bool) ||
(object is num) ||
(object is String)) {
throw new ArgumentError(object);
}
}
get _size => _data.length;
get _limit => (3 * (_size ~/ 4));
List _data;
int _used; // Number of used (active and deleted) slots.
}

View file

@ -5,6 +5,7 @@
#include "vm/bootstrap_natives.h"
#include "vm/exceptions.h"
#include "vm/heap.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/stack_frame.h"
@ -21,6 +22,22 @@ DEFINE_NATIVE_ENTRY(Object_cid, 1) {
}
DEFINE_NATIVE_ENTRY(Object_getHash, 1) {
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
Heap* heap = isolate->heap();
return Smi::New(heap->GetHash(instance.raw()));
}
DEFINE_NATIVE_ENTRY(Object_setHash, 2) {
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, hash, arguments->NativeArgAt(1));
Heap* heap = isolate->heap();
heap->SetHash(instance.raw(), hash.Value());
return Object::null();
}
DEFINE_NATIVE_ENTRY(Object_toString, 1) {
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
const char* c_str = instance.ToCString();

View file

@ -4,20 +4,26 @@
patch class Object {
// Helpers used to implement hashCode. If a hashCode is used we remember it
// using an Expando object. A new hashCode value is calculated using a Random
// Helpers used to implement hashCode. If a hashCode is used, we remember it
// in a weak table in the VM. A new hashCode value is calculated using a
// number generator.
static Expando _hashCodeExp = new Expando("Object.hashCode");
static Random _hashCodeRnd = new Random();
static final _hashCodeRnd = new Random();
static _getHash(obj) native "Object_getHash";
static _setHash(obj, hash) native "Object_setHash";
/* patch */ int get hashCode {
if (this == null) {
return 2011; // The year Dart was announced and a prime.
}
var result = _hashCodeExp[this];
if (result == null) {
result = _hashCodeRnd.nextInt(0x40000000); // Stay in Smi range.
_hashCodeExp[this] = result;
var result = _getHash(this);
if (result == 0) {
// We want the hash to be a Smi value greater than 0.
result = _hashCodeRnd.nextInt(0x40000000);
while (result == 0) {
result = _hashCodeRnd.nextInt(0x40000000);
}
_setHash(this, result);
}
return result;
}

View file

@ -13,6 +13,8 @@ namespace dart {
// List of bootstrap native entry points used in the core dart library.
#define BOOTSTRAP_NATIVE_LIST(V) \
V(Object_getHash, 1) \
V(Object_setHash, 2) \
V(Object_toString, 1) \
V(Object_noSuchMethod, 6) \
V(Object_runtimeType, 1) \

View file

@ -379,18 +379,20 @@ void GCMarker::ProcessWeakProperty(RawWeakProperty* raw_weak,
}
void GCMarker::ProcessPeerReferents(PageSpace* page_space) {
PageSpace::PeerTable* peer_table = page_space->GetPeerTable();
PageSpace::PeerTable::iterator it = peer_table->begin();
while (it != peer_table->end()) {
RawObject* raw_obj = it->first;
ASSERT(raw_obj->IsHeapObject());
if (raw_obj->IsMarked()) {
// The object has survived. Do nothing.
++it;
} else {
// The object has become garbage. Remove its record.
peer_table->erase(it++);
void GCMarker::ProcessWeakTables(PageSpace* page_space) {
for (Heap::WeakSelector sel = static_cast<Heap::WeakSelector>(0);
sel < Heap::kNumWeakSelectors;
sel++) {
WeakTable* table = heap_->GetWeakTable(Heap::kOld, sel);
intptr_t size = table->size();
for (intptr_t i = 0; i < size; i++) {
if (table->IsValidEntryAt(i)) {
RawObject* raw_obj = table->ObjectAt(i);
ASSERT(raw_obj->IsHeapObject());
if (!raw_obj->IsMarked()) {
table->InvalidateAt(i);
}
}
}
}
}
@ -408,7 +410,7 @@ void GCMarker::MarkObjects(Isolate* isolate,
MarkingWeakVisitor mark_weak;
IterateWeakRoots(isolate, &mark_weak, invoke_api_callbacks);
mark.Finalize();
ProcessPeerReferents(page_space);
ProcessWeakTables(page_space);
Epilogue(isolate, invoke_api_callbacks);
}

View file

@ -41,7 +41,7 @@ class GCMarker : public ValueObject {
void IterateWeakReferences(Isolate* isolate, MarkingVisitor* visitor);
void DrainMarkingStack(Isolate* isolate, MarkingVisitor* visitor);
void ProcessWeakProperty(RawWeakProperty* raw_weak, MarkingVisitor* visitor);
void ProcessPeerReferents(PageSpace* page_space);
void ProcessWeakTables(PageSpace* page_space);
Heap* heap_;

View file

@ -19,6 +19,7 @@
#include "vm/stack_frame.h"
#include "vm/verifier.h"
#include "vm/virtual_memory.h"
#include "vm/weak_table.h"
namespace dart {
@ -35,7 +36,13 @@ DEFINE_FLAG(int, old_gen_heap_size, Heap::kHeapSizeInMB,
"old gen heap size in MB,"
"e.g: --old_gen_heap_size=1024 allocates a 1024MB old gen heap");
Heap::Heap() : read_only_(false), gc_in_progress_(false) {
Heap::Heap() : read_only_(false), gc_in_progress_(false) {
for (WeakSelector sel = static_cast<WeakSelector>(0);
sel < kNumWeakSelectors;
sel++) {
new_weak_tables_[sel] = new WeakTable(0);
old_weak_tables_[sel] = new WeakTable(0);
}
new_space_ = new Scavenger(this,
(FLAG_new_gen_heap_size * MB),
kNewObjectAlignmentOffset);
@ -360,27 +367,33 @@ const char* Heap::GCReasonToString(GCReason gc_reason) {
}
void Heap::SetPeer(RawObject* raw_obj, void* peer) {
if (raw_obj->IsNewObject()) {
new_space_->SetPeer(raw_obj, peer);
} else {
ASSERT(raw_obj->IsOldObject());
old_space_->SetPeer(raw_obj, peer);
}
int64_t Heap::PeerCount() const {
return new_weak_tables_[kPeers]->count() + old_weak_tables_[kPeers]->count();
}
void* Heap::GetPeer(RawObject* raw_obj) {
int64_t Heap::HashCount() const {
return
new_weak_tables_[kHashes]->count() + old_weak_tables_[kHashes]->count();
}
intptr_t Heap::GetWeakEntry(RawObject* raw_obj, WeakSelector sel) const {
if (raw_obj->IsNewObject()) {
return new_space_->GetPeer(raw_obj);
return new_weak_tables_[sel]->GetValue(raw_obj);
}
ASSERT(raw_obj->IsOldObject());
return old_space_->GetPeer(raw_obj);
return old_weak_tables_[sel]->GetValue(raw_obj);
}
int64_t Heap::PeerCount() const {
return new_space_->PeerCount() + old_space_->PeerCount();
void Heap::SetWeakEntry(RawObject* raw_obj, WeakSelector sel, intptr_t val) {
if (raw_obj->IsNewObject()) {
new_weak_tables_[sel] = new_weak_tables_[sel]->SetValue(raw_obj, val);
} else {
ASSERT(raw_obj->IsOldObject());
old_weak_tables_[sel] = old_weak_tables_[sel]->SetValue(raw_obj, val);
}
}

View file

@ -11,6 +11,7 @@
#include "vm/globals.h"
#include "vm/pages.h"
#include "vm/scavenger.h"
#include "vm/weak_table.h"
namespace dart {
@ -33,6 +34,12 @@ class Heap {
kCode,
};
enum WeakSelector {
kPeers = 0,
kHashes,
kNumWeakSelectors
};
enum ApiCallbacks {
kIgnoreApiCallbacks,
kInvokeApiCallbacks
@ -168,17 +175,45 @@ class Heap {
static const char* GCReasonToString(GCReason gc_reason);
// Associates a peer with an object. If an object has a peer, it is
// replaced. A value of NULL disassociate an object from its peer.
void SetPeer(RawObject* raw_obj, void* peer);
// Retrieves the peer associated with an object. Returns NULL if
// there is no association.
void* GetPeer(RawObject* raw_obj);
// Returns the number of objects with a peer.
// Associate a peer with an object. A non-existent peer is equal to NULL.
void SetPeer(RawObject* raw_obj, void* peer) {
SetWeakEntry(raw_obj, kPeers, reinterpret_cast<intptr_t>(peer));
}
void* GetPeer(RawObject* raw_obj) const {
return reinterpret_cast<void*>(GetWeakEntry(raw_obj, kPeers));
}
int64_t PeerCount() const;
// Associate an identity hashCode with an object. An non-existent hashCode
// is equal to 0.
void SetHash(RawObject* raw_obj, intptr_t hash) {
SetWeakEntry(raw_obj, kHashes, hash);
}
intptr_t GetHash(RawObject* raw_obj) const {
return GetWeakEntry(raw_obj, kHashes);
}
int64_t HashCount() const;
// Used by the GC algorithms to propagate weak entries.
intptr_t GetWeakEntry(RawObject* raw_obj, WeakSelector sel) const;
void SetWeakEntry(RawObject* raw_obj, WeakSelector sel, intptr_t val);
WeakTable* GetWeakTable(Space space, WeakSelector selector) const {
if (space == kNew) {
return new_weak_tables_[selector];
}
ASSERT(space ==kOld);
return old_weak_tables_[selector];
}
void SetWeakTable(Space space, WeakSelector selector, WeakTable* value) {
if (space == kNew) {
new_weak_tables_[selector] = value;
} else {
ASSERT(space == kOld);
old_weak_tables_[selector] = value;
}
}
// Stats collection.
void RecordTime(int id, int64_t micros) {
ASSERT((id >= 0) && (id < GCStats::kDataEntries));
@ -246,6 +281,9 @@ class Heap {
Scavenger* new_space_;
PageSpace* old_space_;
WeakTable* new_weak_tables_[kNumWeakSelectors];
WeakTable* old_weak_tables_[kNumWeakSelectors];
// GC stats collection.
GCStats stats_;

View file

@ -321,26 +321,6 @@ void PageSpace::VisitObjects(ObjectVisitor* visitor) const {
}
void PageSpace::SetPeer(RawObject* raw_obj, void* peer) {
if (peer == NULL) {
peer_table_.erase(raw_obj);
} else {
peer_table_[raw_obj] = peer;
}
}
void* PageSpace::GetPeer(RawObject* raw_obj) {
PeerTable::iterator it = peer_table_.find(raw_obj);
return (it == peer_table_.end()) ? NULL : it->second;
}
int64_t PageSpace::PeerCount() const {
return static_cast<int64_t>(peer_table_.size());
}
void PageSpace::VisitObjectPointers(ObjectPointerVisitor* visitor) const {
HeapPage* page = pages_;
while (page != NULL) {

View file

@ -5,8 +5,6 @@
#ifndef VM_PAGES_H_
#define VM_PAGES_H_
#include <map>
#include "vm/freelist.h"
#include "vm/globals.h"
#include "vm/virtual_memory.h"
@ -205,16 +203,6 @@ class PageSpace {
void WriteProtect(bool read_only);
typedef std::map<RawObject*, void*> PeerTable;
void SetPeer(RawObject* raw_obj, void* peer);
void* GetPeer(RawObject* raw_obj);
int64_t PeerCount() const;
PeerTable* GetPeerTable() { return &peer_table_; }
private:
// Ids for time and data records in Heap::GCStats.
enum {
@ -253,8 +241,6 @@ class PageSpace {
HeapPage* pages_tail_;
HeapPage* large_pages_;
PeerTable peer_table_;
// Various sizes being tracked for this generation.
intptr_t max_capacity_;
intptr_t capacity_;

View file

@ -16,6 +16,7 @@
#include "vm/store_buffer.h"
#include "vm/verifier.h"
#include "vm/visitor.h"
#include "vm/weak_table.h"
namespace dart {
@ -556,19 +557,26 @@ uword Scavenger::ProcessWeakProperty(RawWeakProperty* raw_weak,
}
void Scavenger::ProcessPeerReferents() {
PeerTable prev;
std::swap(prev, peer_table_);
for (PeerTable::iterator it = prev.begin(); it != prev.end(); ++it) {
RawObject* raw_obj = it->first;
ASSERT(raw_obj->IsHeapObject());
uword raw_addr = RawObject::ToAddr(raw_obj);
uword header = *reinterpret_cast<uword*>(raw_addr);
if (IsForwarding(header)) {
// The object has survived. Preserve its record.
uword new_addr = ForwardedAddr(header);
raw_obj = RawObject::FromAddr(new_addr);
heap_->SetPeer(raw_obj, it->second);
void Scavenger::ProcessWeakTables() {
for (Heap::WeakSelector sel = static_cast<Heap::WeakSelector>(0);
sel < Heap::kNumWeakSelectors;
sel++) {
WeakTable* table = heap_->GetWeakTable(Heap::kNew, sel);
intptr_t size = table->size();
heap_->SetWeakTable(Heap::kNew, sel, WeakTable::NewFrom(table));
for (intptr_t i = 0; i < size; i++) {
if (table->IsValidEntryAt(i)) {
RawObject* raw_obj = table->ObjectAt(i);
ASSERT(raw_obj->IsHeapObject());
uword raw_addr = RawObject::ToAddr(raw_obj);
uword header = *reinterpret_cast<uword*>(raw_addr);
if (IsForwarding(header)) {
// The object has survived. Preserve its record.
uword new_addr = ForwardedAddr(header);
raw_obj = RawObject::FromAddr(new_addr);
heap_->SetWeakEntry(raw_obj, sel, table->ValueAt(i));
}
}
}
}
}
@ -625,7 +633,7 @@ void Scavenger::Scavenge(bool invoke_api_callbacks) {
ScavengerWeakVisitor weak_visitor(this);
IterateWeakRoots(isolate, &weak_visitor, invoke_api_callbacks);
visitor.Finalize();
ProcessPeerReferents();
ProcessWeakTables();
int64_t end = OS::GetCurrentTimeMicros();
heap_->RecordTime(kProcessToSpace, middle - start);
heap_->RecordTime(kIterateWeaks, end - middle);
@ -649,23 +657,4 @@ void Scavenger::WriteProtect(bool read_only) {
}
void Scavenger::SetPeer(RawObject* raw_obj, void* peer) {
if (peer == NULL) {
peer_table_.erase(raw_obj);
} else {
peer_table_[raw_obj] = peer;
}
}
void* Scavenger::GetPeer(RawObject* raw_obj) {
PeerTable::iterator it = peer_table_.find(raw_obj);
return (it == peer_table_.end()) ? NULL : it->second;
}
int64_t Scavenger::PeerCount() const {
return static_cast<int64_t>(peer_table_.size());
}
} // namespace dart

View file

@ -5,8 +5,6 @@
#ifndef VM_SCAVENGER_H_
#define VM_SCAVENGER_H_
#include <map>
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/flags.h"
@ -89,12 +87,6 @@ class Scavenger {
void WriteProtect(bool read_only);
void SetPeer(RawObject* raw_obj, void* peer);
void* GetPeer(RawObject* raw_obj);
int64_t PeerCount() const;
private:
// Ids for time and data records in Heap::GCStats.
enum {
@ -145,7 +137,7 @@ class Scavenger {
return end_ < to_->end();
}
void ProcessPeerReferents();
void ProcessWeakTables();
VirtualMemory* space_;
MemoryRegion* to_;
@ -153,9 +145,6 @@ class Scavenger {
Heap* heap_;
typedef std::map<RawObject*, void*> PeerTable;
PeerTable peer_table_;
// Current allocation top and end. These values are being accessed directly
// from generated code.
uword top_;

View file

@ -335,6 +335,8 @@
'visitor.h',
'vtune.cc',
'vtune.h',
'weak_table.cc',
'weak_table.h',
'zone.cc',
'zone.h',
'zone_test.cc',

72
runtime/vm/weak_table.cc Normal file
View file

@ -0,0 +1,72 @@
// 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.
#include "vm/weak_table.h"
#include "platform/assert.h"
#include "vm/raw_object.h"
namespace dart {
WeakTable* WeakTable::SetValue(RawObject* key, intptr_t val) {
intptr_t sz = size();
intptr_t idx = Hash(key) % sz;
intptr_t empty_idx = -1;
RawObject* obj = ObjectAt(idx);
while (obj != NULL) {
if (obj == key) {
SetValueAt(idx, val);
return this;
} else if ((empty_idx < 0) &&
(reinterpret_cast<intptr_t>(obj) == kDeletedEntry)) {
empty_idx = idx; // Insert at this location if not found.
}
idx = (idx + 1) % sz;
obj = ObjectAt(idx);
}
if (val == 0) {
// Do not enter an invalid value. Associating 0 with a key deletes it from
// this weak table above in SetValueAt. If the key was not present in the
// weak table we are done.
return this;
}
if (empty_idx >= 0) {
// We will be reusing a slot below.
set_used(used() - 1);
idx = empty_idx;
}
ASSERT(!IsValidEntryAt(idx));
// Set the key and value.
SetObjectAt(idx, key);
SetValueAt(idx, val);
// Update the counts.
set_used(used() + 1);
set_count(count() + 1);
// Rehash if needed to ensure that there are empty slots available.
if (used_ >= limit()) {
return Rehash();
}
return this;
}
WeakTable* WeakTable::Rehash() {
intptr_t sz = size();
WeakTable* result = NewFrom(this);
for (intptr_t i = 0; i < sz; i++) {
if (IsValidEntryAt(i)) {
WeakTable* temp = result->SetValue(ObjectAt(i), ValueAt(i));
ASSERT(temp == result);
}
}
return result;
}
} // namespace dart

163
runtime/vm/weak_table.h Normal file
View file

@ -0,0 +1,163 @@
// 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:
explicit WeakTable(intptr_t size) : used_(0), count_(0) {
ASSERT(size >= 0);
if (size < kMinSize) {
size = kMinSize;
}
data_ = reinterpret_cast<intptr_t*>(calloc(size, kEntrySize * kWordSize));
size_ = size;
}
static WeakTable* NewFrom(WeakTable* original) {
intptr_t cnt = original->count();
intptr_t sz = original->size();
intptr_t new_sz = sz;
if (cnt <= (sz / 4)) {
// Reduce the capacity.
new_sz = sz / 2;
} else if (cnt > (sz / 2)) {
// Increase the capacity.
new_sz = sz * 2;
if (new_sz < sz) {
FATAL("Reached impossible state of having more weak table entries"
" than memory available for heap objects.");
}
}
return new WeakTable(new_sz);
}
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 {
return reinterpret_cast<RawObject*>(data_[ObjectIndex(i)]);
}
intptr_t ValueAt(intptr_t i) const {
return data_[ValueIndex(i)];
}
WeakTable* SetValue(RawObject* key, intptr_t val);
intptr_t GetValue(RawObject* key) const {
intptr_t sz = size();
intptr_t idx = Hash(key) % sz;
RawObject* obj = ObjectAt(idx);
while (obj != NULL) {
if (obj == key) {
return ValueAt(idx);
}
idx = (idx + 1) % sz;
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 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 {
ASSERT(i >= 0);
ASSERT(i < size());
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) {
data_[ObjectIndex(i)] = reinterpret_cast<intptr_t>(key);
}
void SetValueAt(intptr_t i, intptr_t val) {
// 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;
}
WeakTable* Rehash();
static intptr_t Hash(RawObject* key) {
return reinterpret_cast<intptr_t>(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_IMPLICIT_CONSTRUCTORS(WeakTable);
};
} // namespace dart
#endif // VM_WEAK_TABLE_H_