Add filter to SkTHashTable and SkTHashMap

Filter takes a bool function where fales means remove the entry.

Change-Id: I768baed6a4e26308f571b0b595ae19aea9d8dca7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/259821
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
Herb Derby 2019-12-12 15:07:11 -05:00 committed by Skia Commit-Bot
parent dcd8e9389d
commit 2fc9fa6d08
2 changed files with 91 additions and 30 deletions

View File

@ -104,39 +104,11 @@ public:
Slot& s = fSlots[index];
SkASSERT(!s.empty());
if (hash == s.hash && key == Traits::GetKey(s.val)) {
fCount--;
break;
this->removeSlot(index);
return;
}
index = this->next(index);
}
// Rearrange elements to restore the invariants for linear probing.
for (;;) {
Slot& emptySlot = fSlots[index];
int emptyIndex = index;
int originalIndex;
// Look for an element that can be moved into the empty slot.
// If the empty slot is in between where an element landed, and its native slot, then
// move it to the empty slot. Don't move it if its native slot is in between where
// the element landed and the empty slot.
// [native] <= [empty] < [candidate] == GOOD, can move candidate to empty slot
// [empty] < [native] < [candidate] == BAD, need to leave candidate where it is
do {
index = this->next(index);
Slot& s = fSlots[index];
if (s.empty()) {
// We're done shuffling elements around. Clear the last empty slot.
emptySlot = Slot();
return;
}
originalIndex = s.hash & (fCapacity - 1);
} while ((index <= originalIndex && originalIndex < emptyIndex)
|| (originalIndex < emptyIndex && emptyIndex < index)
|| (emptyIndex < index && index <= originalIndex));
// Move the element to the empty slot.
Slot& moveFrom = fSlots[index];
emptySlot = std::move(moveFrom);
}
}
// Call fn on every entry in the table. You may mutate the entries, but be very careful.
@ -159,6 +131,25 @@ public:
}
}
// Call fn on every entry in the table. Fn can return false to remove the entry. You may mutate
// the entries, but be very careful.
template <typename Fn> // f(T*)
void mutate(Fn&& fn) {
for (int i = 0; i < fCapacity;) {
bool keep = true;
if (!fSlots[i].empty()) {
keep = fn(&fSlots[i].val);
}
if (keep) {
i++;
} else {
this->removeSlot(i);
// Something may now have moved into slot i, so we'll loop
// around to check slot i again.
}
}
}
private:
T* uncheckedSet(T&& val) {
const K& key = Traits::GetKey(val);
@ -204,6 +195,38 @@ private:
SkASSERT(fCount == oldCount);
}
void removeSlot(int index) {
fCount--;
// Rearrange elements to restore the invariants for linear probing.
for (;;) {
Slot& emptySlot = fSlots[index];
int emptyIndex = index;
int originalIndex;
// Look for an element that can be moved into the empty slot.
// If the empty slot is in between where an element landed, and its native slot, then
// move it to the empty slot. Don't move it if its native slot is in between where
// the element landed and the empty slot.
// [native] <= [empty] < [candidate] == GOOD, can move candidate to empty slot
// [empty] < [native] < [candidate] == BAD, need to leave candidate where it is
do {
index = this->next(index);
Slot& s = fSlots[index];
if (s.empty()) {
// We're done shuffling elements around. Clear the last empty slot.
emptySlot = Slot();
return;
}
originalIndex = s.hash & (fCapacity - 1);
} while ((index <= originalIndex && originalIndex < emptyIndex)
|| (originalIndex < emptyIndex && emptyIndex < index)
|| (emptyIndex < index && index <= originalIndex));
// Move the element to the empty slot.
Slot& moveFrom = fSlots[index];
emptySlot = std::move(moveFrom);
}
}
int next(int index) const {
index--;
if (index < 0) { index += fCapacity; }
@ -299,6 +322,13 @@ public:
fTable.foreach([&fn](const Pair& p){ fn(p.key, p.val); });
}
// Call fn on every key/value pair in the table. Fn may return false to remove the entry. You
// may mutate the value but not the key.
template <typename Fn> // f(K, V*) or f(const K&, V*)
void mutate(Fn&& fn) {
fTable.mutate([&fn](Pair* p) { return fn(p->key, &p->val); });
}
private:
struct Pair {
K key;

View File

@ -199,3 +199,34 @@ DEF_TEST(HashFindOrNull, r) {
REPORTER_ASSERT(r, &seven == table.findOrNull(7));
}
DEF_TEST(HashFilter, r) {
struct HashTraits {
static int GetKey(const int e) { return e; }
static uint32_t Hash(int key) { return 0; }
};
SkTHashTable<int, int, HashTraits> table;
for (int i = 0; i < 10; i++) {
table.set(i);
}
REPORTER_ASSERT(r, table.count() == 10);
table.mutate([](int* i) {
// Do not remove.
return true;
});
REPORTER_ASSERT(r, table.count() == 10);
table.mutate([](int* i) {
// table.remove(*i);
return false;
});
REPORTER_ASSERT(r, table.count() == 0);
}