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:
parent
dcd8e9389d
commit
2fc9fa6d08
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user