Skip to content

Commit 3f5c8cf

Browse files
nikicAlexisPerry
authored andcommitted
[SmallPtrSet] Add remove_if() method (llvm#96468)
Add remove_if() method, similar to the one already present on SetVector. It is intended to replace the following pattern: for (Foo *Ptr : Set) if (Pred(Ptr)) Set.erase(Ptr); With: Set.remove_if(Pred); This pattern is commonly used for set intersection, where `Pred` is something like `!OtherSet.contains(Ptr)`. The implementation provided here is a bit more efficient than the naive loop, because it does not require looking up the bucket during the erase() operation again. However, my actual motivation for this is to have a way to perform this operation without relying on the current `std::set`-style guarantee that erase() does not invalidate iterators. I'd like to stop making use of tombstones in the small regime, which will make insertion operations a good bit more efficient. However, this will invalidate iterators during erase().
1 parent dfe4eb9 commit 3f5c8cf

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

llvm/include/llvm/ADT/SmallPtrSet.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,22 @@ class SmallPtrSetImpl : public SmallPtrSetImplBase {
356356
bool erase(PtrType Ptr) {
357357
return erase_imp(PtrTraits::getAsVoidPointer(Ptr));
358358
}
359+
360+
/// Remove elements that match the given predicate.
361+
template <typename UnaryPredicate>
362+
void remove_if(UnaryPredicate P) {
363+
for (const void **APtr = CurArray, **E = EndPointer(); APtr != E; ++APtr) {
364+
const void *Value = *APtr;
365+
if (Value == getTombstoneMarker() || Value == getEmptyMarker())
366+
continue;
367+
PtrType Ptr = PtrTraits::getFromVoidPointer(const_cast<void *>(Value));
368+
if (P(Ptr)) {
369+
*APtr = getTombstoneMarker();
370+
++NumTombstones;
371+
}
372+
}
373+
}
374+
359375
/// count - Return 1 if the specified pointer is in the set, 0 otherwise.
360376
size_type count(ConstPtrType Ptr) const {
361377
return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer();

llvm/unittests/ADT/SmallPtrSetTest.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,36 @@ TEST(SmallPtrSetTest, InsertIterator) {
409409
for (const auto *Ptr : Buf)
410410
EXPECT_TRUE(Set.contains(Ptr));
411411
}
412+
413+
TEST(SmallPtrSetTest, RemoveIf) {
414+
SmallPtrSet<int *, 5> Set;
415+
int Vals[6] = {0, 1, 2, 3, 4, 5};
416+
417+
// Stay in small regime.
418+
Set.insert(&Vals[0]);
419+
Set.insert(&Vals[1]);
420+
Set.insert(&Vals[2]);
421+
Set.insert(&Vals[3]);
422+
Set.erase(&Vals[0]); // Leave a tombstone.
423+
424+
// Remove odd elements.
425+
Set.remove_if([](int *Ptr) { return *Ptr % 2 != 0; });
426+
// We should only have element 2 left now.
427+
EXPECT_EQ(Set.size(), 1u);
428+
EXPECT_TRUE(Set.contains(&Vals[2]));
429+
430+
// Switch to big regime.
431+
Set.insert(&Vals[0]);
432+
Set.insert(&Vals[1]);
433+
Set.insert(&Vals[3]);
434+
Set.insert(&Vals[4]);
435+
Set.insert(&Vals[5]);
436+
Set.erase(&Vals[0]); // Leave a tombstone.
437+
438+
// Remove odd elements.
439+
Set.remove_if([](int *Ptr) { return *Ptr % 2 != 0; });
440+
// We should only have elements 2 and 4 left now.
441+
EXPECT_EQ(Set.size(), 2u);
442+
EXPECT_TRUE(Set.contains(&Vals[2]));
443+
EXPECT_TRUE(Set.contains(&Vals[4]));
444+
}

0 commit comments

Comments
 (0)