Index: runtime/vm/hash_table.h |
=================================================================== |
--- runtime/vm/hash_table.h (revision 38793) |
+++ runtime/vm/hash_table.h (working copy) |
@@ -43,17 +43,22 @@ |
// |
// The classes all wrap an Array handle, and metods like HashSet::Insert can |
// trigger growth into a new RawArray, updating the handle. Debug mode asserts |
-// that 'Release' was called to get the final RawArray before destruction. |
+// that 'Release' was called once to access the final array before destruction. |
// |
// Example use: |
-// typedef UnorderedHashMap<FooTraits> ResolvedNamesMap; |
+// typedef UnorderedHashMap<FooTraits> FooMap; |
// ... |
-// ResolvedNamesMap cache(Array::Handle(resolved_names())); |
+// FooMap cache(get_foo_cache()); |
// cache.UpdateOrInsert(name0, obj0); |
// cache.UpdateOrInsert(name1, obj1); |
// ... |
-// StorePointer(&raw_ptr()->resolved_names_, cache.Release()); |
+// set_foo_cache(cache.Release()); |
// |
+// If you *know* that no mutating operations were called, you can optimize: |
+// ... |
+// obj ^= cache.GetOrNull(name); |
+// ASSERT(cache.Release().raw() == get_foo_cache()); |
+// |
// TODO(koda): When exposing these to Dart code, document and assert that |
// KeyTraits methods must not run Dart code (since the C++ code doesn't check |
// for concurrent modification). |
@@ -79,17 +84,25 @@ |
class HashTable : public ValueObject { |
public: |
typedef KeyTraits Traits; |
- explicit HashTable(Array& data) : data_(data) {} |
+ // Uses 'isolate' for handle allocation. 'Release' must be called at the end |
+ // to obtain the final table after potential growth/shrinkage. |
+ HashTable(Isolate* isolate, RawArray* data) |
+ : isolate_(isolate), data_(&Array::Handle(isolate_, data)) {} |
+ // Like above, except uses current isolate. |
+ explicit HashTable(RawArray* data) |
+ : isolate_(Isolate::Current()), data_(&Array::Handle(isolate_, data)) {} |
- RawArray* Release() { |
- ASSERT(!data_.IsNull()); |
- RawArray* array = data_.raw(); |
- data_ = Array::null(); |
- return array; |
+ Array& Release() { |
+ ASSERT(data_ != NULL); |
+ Array* result = data_; |
+ // Ensure that no methods are called after 'Release'. |
+ data_ = NULL; |
+ return *result; |
} |
~HashTable() { |
- ASSERT(data_.IsNull()); |
+ // Ensure that 'Release' was called. |
+ ASSERT(data_ == NULL); |
} |
// Returns a backing storage size such that 'num_occupied' distinct keys can |
@@ -103,12 +116,12 @@ |
// Initializes an empty table. |
void Initialize() const { |
- ASSERT(data_.Length() >= ArrayLengthForNumOccupied(0)); |
- Smi& zero = Smi::Handle(Smi::New(0)); |
- data_.SetAt(kOccupiedEntriesIndex, zero); |
- data_.SetAt(kDeletedEntriesIndex, zero); |
- for (intptr_t i = kHeaderSize; i < data_.Length(); ++i) { |
- data_.SetAt(i, Object::sentinel()); |
+ ASSERT(data_->Length() >= ArrayLengthForNumOccupied(0)); |
+ Smi& zero = Smi::Handle(isolate(), Smi::New(0)); |
+ data_->SetAt(kOccupiedEntriesIndex, zero); |
+ data_->SetAt(kDeletedEntriesIndex, zero); |
+ for (intptr_t i = kHeaderSize; i < data_->Length(); ++i) { |
+ data_->SetAt(i, Object::sentinel()); |
} |
} |
@@ -124,7 +137,7 @@ |
ASSERT(NumOccupied() < NumEntries()); |
// TODO(koda): Add salt. |
intptr_t probe = static_cast<uword>(KeyTraits::Hash(key)) % NumEntries(); |
- Object& obj = Object::Handle(); |
+ Object& obj = Object::Handle(isolate()); |
// TODO(koda): Consider quadratic probing. |
for (; ; probe = (probe + 1) % NumEntries()) { |
if (IsUnused(probe)) { |
@@ -151,7 +164,7 @@ |
ASSERT(entry != NULL); |
ASSERT(NumOccupied() < NumEntries()); |
intptr_t probe = static_cast<uword>(KeyTraits::Hash(key)) % NumEntries(); |
- Object& obj = Object::Handle(); |
+ Object& obj = Object::Handle(isolate()); |
intptr_t deleted = -1; |
// TODO(koda): Consider quadratic probing. |
for (; ; probe = (probe + 1) % NumEntries()) { |
@@ -205,14 +218,14 @@ |
} |
RawObject* GetPayload(intptr_t entry, intptr_t component) const { |
ASSERT(IsOccupied(entry)); |
- return data_.At(PayloadIndex(entry, component)); |
+ return data_->At(PayloadIndex(entry, component)); |
} |
void UpdatePayload(intptr_t entry, |
intptr_t component, |
const Object& value) const { |
ASSERT(IsOccupied(entry)); |
ASSERT(0 <= component && component < kPayloadSize); |
- data_.SetAt(PayloadIndex(entry, component), value); |
+ data_->SetAt(PayloadIndex(entry, component), value); |
} |
// Deletes both the key and payload of the specified entry. |
void DeleteEntry(intptr_t entry) const { |
@@ -225,7 +238,7 @@ |
AdjustSmiValueAt(kDeletedEntriesIndex, 1); |
} |
intptr_t NumEntries() const { |
- return (data_.Length() - kFirstKeyIndex) / kEntrySize; |
+ return (data_->Length() - kFirstKeyIndex) / kEntrySize; |
} |
intptr_t NumUnused() const { |
return NumEntries() - NumOccupied() - NumDeleted(); |
@@ -256,29 +269,34 @@ |
} |
RawObject* InternalGetKey(intptr_t entry) const { |
- return data_.At(KeyIndex(entry)); |
+ return data_->At(KeyIndex(entry)); |
} |
void InternalSetKey(intptr_t entry, const Object& key) const { |
- data_.SetAt(KeyIndex(entry), key); |
+ data_->SetAt(KeyIndex(entry), key); |
} |
intptr_t GetSmiValueAt(intptr_t index) const { |
- ASSERT(Object::Handle(data_.At(index)).IsSmi()); |
- return Smi::Value(Smi::RawCast(data_.At(index))); |
+ ASSERT(Object::Handle(isolate(), data_->At(index)).IsSmi()); |
+ return Smi::Value(Smi::RawCast(data_->At(index))); |
} |
void SetSmiValueAt(intptr_t index, intptr_t value) const { |
- const Smi& smi = Smi::Handle(Smi::New(value)); |
- data_.SetAt(index, smi); |
+ const Smi& smi = Smi::Handle(isolate(), Smi::New(value)); |
+ data_->SetAt(index, smi); |
} |
void AdjustSmiValueAt(intptr_t index, intptr_t delta) const { |
SetSmiValueAt(index, (GetSmiValueAt(index) + delta)); |
} |
- Array& data_; |
+ Isolate* isolate() const { return isolate_; } |
+ Isolate* isolate_; |
+ // This is a pointer rather than a reference, to enable Release nulling it, |
+ // preventing post-Release modification. |
+ Array* data_; |
+ |
friend class HashTables; |
}; |
@@ -289,7 +307,9 @@ |
public: |
typedef HashTable<KeyTraits, kUserPayloadSize, 0> BaseTable; |
static const intptr_t kPayloadSize = kUserPayloadSize; |
- explicit UnorderedHashTable(Array& data) : BaseTable(data) {} |
+ explicit UnorderedHashTable(RawArray* data) : BaseTable(data) {} |
+ UnorderedHashTable(Isolate* isolate, RawArray* data) |
+ : BaseTable(isolate, data) {} |
// Note: Does not check for concurrent modification. |
class Iterator { |
public: |
@@ -326,7 +346,9 @@ |
typedef HashTable<KeyTraits, kUserPayloadSize + 1, 1> BaseTable; |
static const intptr_t kPayloadSize = kUserPayloadSize; |
static const intptr_t kNextEnumIndex = BaseTable::kMetaDataIndex; |
- explicit EnumIndexHashTable(Array& data) : BaseTable(data) {} |
+ EnumIndexHashTable(Isolate* isolate, RawArray* data) |
+ : BaseTable(isolate, data) {} |
+ explicit EnumIndexHashTable(RawArray* data) : BaseTable(data) {} |
// Note: Does not check for concurrent modification. |
class Iterator { |
public: |
@@ -369,8 +391,8 @@ |
void InsertKey(intptr_t entry, const Object& key) const { |
BaseTable::InsertKey(entry, key); |
- const Smi& next_enum_index = |
- Smi::Handle(Smi::New(BaseTable::GetSmiValueAt(kNextEnumIndex))); |
+ const Smi& next_enum_index = Smi::Handle(BaseTable::isolate(), |
+ Smi::New(BaseTable::GetSmiValueAt(kNextEnumIndex))); |
BaseTable::UpdatePayload(entry, kPayloadSize, next_enum_index); |
// TODO(koda): Handle possible Smi overflow from repeated insert/delete. |
BaseTable::AdjustSmiValueAt(kNextEnumIndex, 1); |
@@ -386,10 +408,10 @@ |
template<typename Table> |
static RawArray* New(intptr_t initial_capacity, |
Heap::Space space = Heap::kNew) { |
- Table table(Array::Handle(Array::New( |
- Table::ArrayLengthForNumOccupied(initial_capacity), space))); |
+ Table table(Array::New( |
+ Table::ArrayLengthForNumOccupied(initial_capacity), space)); |
table.Initialize(); |
- return table.Release(); |
+ return table.Release().raw(); |
} |
// Clears 'to' and inserts all elements from 'from', in iteration order. |
@@ -425,11 +447,11 @@ |
} |
double target = (low + high) / 2.0; |
intptr_t new_capacity = (1 + table.NumOccupied()) / target; |
- Table new_table(Array::Handle(New<Table>( |
+ Table new_table(New<Table>( |
new_capacity, |
- table.data_.IsOld() ? Heap::kOld : Heap::kNew))); |
+ table.data_->IsOld() ? Heap::kOld : Heap::kNew)); |
Copy(table, new_table); |
- table.data_ = new_table.Release(); |
+ *table.data_ = new_table.Release().raw(); |
} |
// Serializes a table by concatenating its entries as an array. |
@@ -459,7 +481,8 @@ |
template<typename BaseIterTable> |
class HashMap : public BaseIterTable { |
public: |
- explicit HashMap(Array& data) : BaseIterTable(data) {} |
+ explicit HashMap(RawArray* data) : BaseIterTable(data) {} |
+ HashMap(Isolate* isolate, RawArray* data) : BaseIterTable(isolate, data) {} |
template<typename Key> |
RawObject* GetOrNull(const Key& key, bool* present = NULL) const { |
intptr_t entry = BaseIterTable::FindKey(key); |
@@ -506,7 +529,7 @@ |
EnsureCapacity(); |
intptr_t entry = -1; |
if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
- Object& new_key = Object::Handle( |
+ Object& new_key = Object::Handle(BaseIterTable::isolate(), |
BaseIterTable::BaseTable::Traits::NewKey(key)); |
BaseIterTable::InsertKey(entry, new_key); |
BaseIterTable::UpdatePayload(entry, 0, value_if_absent); |
@@ -539,7 +562,8 @@ |
class UnorderedHashMap : public HashMap<UnorderedHashTable<KeyTraits, 1> > { |
public: |
typedef HashMap<UnorderedHashTable<KeyTraits, 1> > BaseMap; |
- explicit UnorderedHashMap(Array& data) : BaseMap(data) {} |
+ explicit UnorderedHashMap(RawArray* data) : BaseMap(data) {} |
+ UnorderedHashMap(Isolate* isolate, RawArray* data) : BaseMap(isolate, data) {} |
}; |
@@ -547,14 +571,16 @@ |
class EnumIndexHashMap : public HashMap<EnumIndexHashTable<KeyTraits, 1> > { |
public: |
typedef HashMap<EnumIndexHashTable<KeyTraits, 1> > BaseMap; |
- explicit EnumIndexHashMap(Array& data) : BaseMap(data) {} |
+ explicit EnumIndexHashMap(RawArray* data) : BaseMap(data) {} |
+ EnumIndexHashMap(Isolate* isolate, RawArray* data) : BaseMap(isolate, data) {} |
}; |
template<typename BaseIterTable> |
class HashSet : public BaseIterTable { |
public: |
- explicit HashSet(Array& data) : BaseIterTable(data) {} |
+ explicit HashSet(RawArray* data) : BaseIterTable(data) {} |
+ HashSet(Isolate* isolate, RawArray* data) : BaseIterTable(isolate, data) {} |
bool Insert(const Object& key) { |
EnsureCapacity(); |
intptr_t entry = -1; |
@@ -584,7 +610,7 @@ |
EnsureCapacity(); |
intptr_t entry = -1; |
if (!BaseIterTable::FindKeyOrDeletedOrUnused(key, &entry)) { |
- Object& new_key = Object::Handle( |
+ Object& new_key = Object::Handle(BaseIterTable::isolate(), |
BaseIterTable::BaseTable::Traits::NewKey(key)); |
BaseIterTable::InsertKey(entry, new_key); |
return new_key.raw(); |
@@ -625,7 +651,8 @@ |
class UnorderedHashSet : public HashSet<UnorderedHashTable<KeyTraits, 0> > { |
public: |
typedef HashSet<UnorderedHashTable<KeyTraits, 0> > BaseSet; |
- explicit UnorderedHashSet(Array& data) : BaseSet(data) {} |
+ explicit UnorderedHashSet(RawArray* data) : BaseSet(data) {} |
+ UnorderedHashSet(Isolate* isolate, RawArray* data) : BaseSet(isolate, data) {} |
}; |
@@ -633,7 +660,8 @@ |
class EnumIndexHashSet : public HashSet<EnumIndexHashTable<KeyTraits, 0> > { |
public: |
typedef HashSet<EnumIndexHashTable<KeyTraits, 0> > BaseSet; |
- explicit EnumIndexHashSet(Array& data) : BaseSet(data) {} |
+ explicit EnumIndexHashSet(RawArray* data) : BaseSet(data) {} |
+ EnumIndexHashSet(Isolate* isolate, RawArray* data) : BaseSet(isolate, data) {} |
}; |
} // namespace dart |