Author: [email protected]
Date: Fri May 15 00:09:17 2009
New Revision: 1956
Modified:
branches/bleeding_edge/src/hashmap.cc
branches/bleeding_edge/src/hashmap.h
branches/bleeding_edge/test/cctest/test-hashmap.cc
Log:
Add a remove method to the hash map.
Extended the hash map test to also use a heavy collision hash function to
exercise the remove code.
Review URL: http://codereview.chromium.org/113397
Modified: branches/bleeding_edge/src/hashmap.cc
==============================================================================
--- branches/bleeding_edge/src/hashmap.cc (original)
+++ branches/bleeding_edge/src/hashmap.cc Fri May 15 00:09:17 2009
@@ -59,7 +59,7 @@
HashMap::Entry* HashMap::Lookup(void* key, uint32_t hash, bool insert) {
// Find a matching entry.
Entry* p = Probe(key, hash);
- if (p->key != NULL) {
+ if (p->key != NULL) {
return p;
}
@@ -84,6 +84,64 @@
}
+void HashMap::Remove(void* key, uint32_t hash) {
+ // Lookup the entry for the key to remove.
+ Entry *p = Probe(key, hash);
+ if (p->key == NULL) {
+ // Key not found nothing to remove.
+ return;
+ }
+
+ // To remove the entry we need to ensure that it does not create an empty
+ // entry that will cause search for an entry to stop to soon. If all the
+ // entries between the entry to remove and the next empty slot have their
+ // initial position inside this interval clearing the entry to remove
will not
+ // break the search. If while searching for the next empty entry an
entry is
+ // encountered which does not have its initial position between the
entry to
+ // remove and the position looked at this entry can be moved to the
place of
+ // the entry to remove without breaking the search for it and the new
entry to
+ // remove will be its previous position.
+ // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
+
+ // This guarantees loop termination as there is at least one empty entry
so
+ // eventually the removed entyr will have an empty entry after it.
+ ASSERT(occupancy_ < capacity_);
+
+ // p is the candidate entry to clear. q is used to scan forwards.
+ Entry* q = p; // Start at the entry to remove.
+ while (true) {
+ // Move q to the next entry.
+ q = q + 1;
+ if (q == map_end()) {
+ q = map_;
+ }
+
+ // All entries between p and q have their initial position between p
and q
+ // and the entry p can be cleared without breaking the search for these
+ // entries.
+ if (q->key == NULL) {
+ break;
+ }
+
+ // Find the initial position for the entry at position q.
+ Entry* r = map_ + (q->hash & (capacity_ - 1));
+
+ // If the entry at position q has its initial position outside the
range
+ // between p and q it can be moved forward to position p and will
still be
+ // found. There is now a new candidate entry for clearing.
+ if (q > p && (r <= p || r > q) ||
+ q < p && (r <= p && r > q)) {
+ *p = *q;
+ p = q;
+ }
+ }
+
+ // Clear the entry which is allowed to en emptied.
+ p->key = NULL;
+ occupancy_--;
+}
+
+
void HashMap::Clear() {
// Mark all entries as empty.
const Entry* end = map_end();
@@ -119,7 +177,7 @@
const Entry* end = map_end();
ASSERT(map_ <= p && p < end);
- ASSERT(occupancy_ < capacity_); // guarantees loop termination
+ ASSERT(occupancy_ < capacity_); // Guarantees loop termination.
while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) {
p++;
if (p >= end) {
Modified: branches/bleeding_edge/src/hashmap.h
==============================================================================
--- branches/bleeding_edge/src/hashmap.h (original)
+++ branches/bleeding_edge/src/hashmap.h Fri May 15 00:09:17 2009
@@ -75,6 +75,9 @@
// Otherwise, NULL is returned.
Entry* Lookup(void* key, uint32_t hash, bool insert);
+ // Removes the entry with matching key.
+ void Remove(void* key, uint32_t hash);
+
// Empties the hash map (occupancy() == 0).
void Clear();
Modified: branches/bleeding_edge/test/cctest/test-hashmap.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-hashmap.cc (original)
+++ branches/bleeding_edge/test/cctest/test-hashmap.cc Fri May 15 00:09:17
2009
@@ -38,20 +38,28 @@
}
+typedef uint32_t (*IntKeyHash)(uint32_t key);
+
+
class IntSet {
public:
- IntSet() : map_(DefaultMatchFun) {}
+ IntSet(IntKeyHash hash) : hash_(hash), map_(DefaultMatchFun) {}
void Insert(int x) {
CHECK_NE(0, x); // 0 corresponds to (void*)NULL - illegal key value
- HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), Hash(x),
true);
+ HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), hash_(x),
true);
CHECK(p != NULL); // insert is set!
CHECK_EQ(reinterpret_cast<void*>(x), p->key);
// we don't care about p->value
}
+ void Remove(int x) {
+ CHECK_NE(0, x); // 0 corresponds to (void*)NULL - illegal key value
+ map_.Remove(reinterpret_cast<void*>(x), hash_(x));
+ }
+
bool Present(int x) {
- HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), Hash(x),
false);
+ HashMap::Entry* p = map_.Lookup(reinterpret_cast<void*>(x), hash_(x),
false);
if (p != NULL) {
CHECK_EQ(reinterpret_cast<void*>(x), p->key);
}
@@ -73,12 +81,16 @@
private:
HashMap map_;
- static uint32_t Hash(uint32_t key) { return key * 23; }
+ IntKeyHash hash_;
};
-TEST(Set) {
- IntSet set;
+static uint32_t Hash(uint32_t key) { return 23; }
+static uint32_t CollisionHash(uint32_t key) { return key & 0x3; }
+
+
+void TestSet(IntKeyHash hash, int size) {
+ IntSet set(hash);
CHECK_EQ(0, set.occupancy());
set.Insert(1);
@@ -96,6 +108,18 @@
CHECK(!set.Present(4));
CHECK_EQ(3, set.occupancy());
+ set.Remove(1);
+ CHECK(!set.Present(1));
+ CHECK(set.Present(2));
+ CHECK(set.Present(3));
+ CHECK_EQ(2, set.occupancy());
+
+ set.Remove(3);
+ CHECK(!set.Present(1));
+ CHECK(set.Present(2));
+ CHECK(!set.Present(3));
+ CHECK_EQ(1, set.occupancy());
+
set.Clear();
CHECK_EQ(0, set.occupancy());
@@ -103,21 +127,50 @@
const int start = 453;
const int factor = 13;
const int offset = 7;
- const uint32_t n = 1000;
+ const uint32_t n = size;
int x = start;
for (uint32_t i = 0; i < n; i++) {
CHECK_EQ(i, static_cast<double>(set.occupancy()));
set.Insert(x);
- x = x*factor + offset;
+ x = x * factor + offset;
}
+ CHECK_EQ(n, static_cast<double>(set.occupancy()));
// Verify the same sequence of values.
x = start;
for (uint32_t i = 0; i < n; i++) {
CHECK(set.Present(x));
- x = x*factor + offset;
+ x = x * factor + offset;
}
-
CHECK_EQ(n, static_cast<double>(set.occupancy()));
+
+ // Remove all these values.
+ x = start;
+ for (uint32_t i = 0; i < n; i++) {
+ CHECK_EQ(n - i, static_cast<double>(set.occupancy()));
+ CHECK(set.Present(x));
+ set.Remove(x);
+ CHECK(!set.Present(x));
+ x = x * factor + offset;
+
+ // Verify the the expected values are still there.
+ int y = start;
+ for (uint32_t j = 0; j < n; j++) {
+ if (j <= i) {
+ CHECK(!set.Present(y));
+ } else {
+ CHECK(set.Present(y));
+ }
+ y = y * factor + offset;
+ }
+
+ }
+ CHECK_EQ(0, set.occupancy());
+}
+
+
+TEST(Set) {
+ TestSet(Hash, 1000);
+ TestSet(CollisionHash, 100);
}
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---