From 8650e15a377396c93e2a255679351ad19224f8c8 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Sun, 5 Dec 2021 11:11:12 -0800 Subject: [PATCH] [linked-list] add `RemoveAllMatching()` method (#7221) This commit adds a new method `LinkedList::RemoveAllMatching()` which removes all entries in the list matching a given entry indicator from the list and adds them to a new "removed" list. A similar method is also added in `OwningList`. This commit also updates the unit test `test_linked_list` to validate the behavior of the new method. --- src/core/common/linked_list.hpp | 39 ++++++++++++++++++++++ src/core/common/owning_list.hpp | 21 ++++++++++++ tests/unit/test_linked_list.cpp | 58 +++++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/core/common/linked_list.hpp b/src/core/common/linked_list.hpp index d7b2aae49..7c3ee9e5f 100644 --- a/src/core/common/linked_list.hpp +++ b/src/core/common/linked_list.hpp @@ -349,6 +349,45 @@ public: return entry; } + /** + * This template method removes all entries in the list matching a given entry indicator from the list and adds + * them to a new list. + * + * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against entries + * in the list. To check that an entry matches the given indicator, the `Matches()` method is invoked on each + * `Type` entry in the list. The `Matches()` method should be provided by `Type` class accordingly: + * + * bool Type::Matches(const Indicator &aIndicator) const + * + * @param[in] aIndicator An entry indicator to match against entries in the list. + * @param[in] aRemovedList The list to add the removed entries to. + * + */ + template void RemoveAllMatching(const Indicator &aIndicator, LinkedList &aRemovedList) + { + Type *entry; + Type *prev; + Type *next; + + for (prev = nullptr, entry = GetHead(); entry != nullptr; entry = next) + { + next = entry->GetNext(); + + if (entry->Matches(aIndicator)) + { + PopAfter(prev); + aRemovedList.Push(*entry); + + // When the entry is removed from the list + // we keep the `prev` pointer same as before. + } + else + { + prev = entry; + } + } + } + /** * This method searches within the linked list to find an entry and if found returns a pointer to previous entry. * diff --git a/src/core/common/owning_list.hpp b/src/core/common/owning_list.hpp index 969147f11..75d201e8a 100644 --- a/src/core/common/owning_list.hpp +++ b/src/core/common/owning_list.hpp @@ -128,6 +128,27 @@ public: { return OwnedPtr(LinkedList::RemoveMatching(aIndicator)); } + + /** + * This template method removes all entries in the list matching a given entry indicator from the list and adds + * them to a new list. + * + * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against entries + * in the list. To check that an entry matches the given indicator, the `Matches()` method is invoked on each + * `Type` entry in the list. The `Matches()` method should be provided by `Type` class accordingly: + * + * bool Type::Matches(const Indicator &aIndicator) const + * + * The ownership of the removed entries is transferred from the original list to the @p aRemovedList. + * + * @param[in] aIndicator An entry indicator to match against entries in the list. + * @param[in] aRemovedList The list to add the removed entries to. + * + */ + template void RemoveAllMatching(const Indicator &aIndicator, OwningList &aRemovedList) + { + RemoveAllMatching(aIndicator, aRemovedList); + } }; } // namespace ot diff --git a/tests/unit/test_linked_list.cpp b/tests/unit/test_linked_list.cpp index 77e5475f2..65944b52d 100644 --- a/tests/unit/test_linked_list.cpp +++ b/tests/unit/test_linked_list.cpp @@ -50,9 +50,16 @@ struct EntryBase struct Entry : public EntryBase, LinkedListEntry { public: - Entry(const char *aName, uint16_t aId) + enum class Type : uint8_t + { + kAlpha, + kBeta, + }; + + Entry(const char *aName, uint16_t aId, Type aType = Type::kAlpha) : mName(aName) , mId(aId) + , mType(aType) , mWasFreed(false) { } @@ -61,6 +68,7 @@ public: uint16_t GetId(void) const { return mId; } bool Matches(const char *aName) const { return strcmp(mName, aName) == 0; } bool Matches(uint16_t aId) const { return mId == aId; } + bool Matches(Type aType) const { return mType == aType; } void Free(void) { mWasFreed = true; } void ResetTestFlags(void) { mWasFreed = false; } @@ -69,9 +77,13 @@ public: private: const char *mName; uint16_t mId; + Type mType; bool mWasFreed; }; +constexpr Entry::Type kAlphaType = Entry::Type::kAlpha; +constexpr Entry::Type kBetaType = Entry::Type::kBeta; + // This function verifies the content of the linked list matches a given list of entries. void VerifyLinkedListContent(const LinkedList *aList, ...) { @@ -120,9 +132,11 @@ void VerifyLinkedListContent(const LinkedList *aList, ...) void TestLinkedList(void) { - Entry a("a", 1), b("b", 2), c("c", 3), d("d", 4), e("e", 5); + Entry a("a", 1, kAlphaType), b("b", 2, kAlphaType), c("c", 3, kBetaType); + Entry d("d", 4, kBetaType), e("e", 5, kAlphaType), f("f", 6, kBetaType); Entry * prev; LinkedList list; + LinkedList removedList; printf("TestLinkedList\n"); @@ -240,6 +254,46 @@ void TestLinkedList(void) VerifyOrQuit(list.FindMatching(c.GetId(), prev) == nullptr, "succeeded when empty"); VerifyOrQuit(list.RemoveMatching(a.GetName()) == nullptr, "succeeded when empty"); VerifyOrQuit(list.Remove(a) == kErrorNotFound, "succeeded when empty"); + + list.Clear(); + removedList.Clear(); + list.Push(f); + list.Push(e); + list.Push(d); + list.Push(c); + list.Push(b); + list.Push(a); + VerifyLinkedListContent(&list, &a, &b, &c, &d, &e, &f, nullptr); + + list.RemoveAllMatching(kAlphaType, removedList); + VerifyLinkedListContent(&list, &c, &d, &f, nullptr); + VerifyLinkedListContent(&removedList, &e, &b, &a, nullptr); + + removedList.Clear(); + list.RemoveAllMatching(kAlphaType, removedList); + VerifyLinkedListContent(&list, &c, &d, &f, nullptr); + VerifyOrQuit(removedList.IsEmpty()); + + list.RemoveAllMatching(kBetaType, removedList); + VerifyOrQuit(list.IsEmpty()); + VerifyLinkedListContent(&removedList, &f, &d, &c, nullptr); + + removedList.Clear(); + list.RemoveAllMatching(kAlphaType, removedList); + VerifyOrQuit(list.IsEmpty()); + VerifyOrQuit(removedList.IsEmpty()); + + list.Push(f); + list.Push(e); + list.Push(d); + list.Push(c); + list.Push(b); + list.Push(a); + VerifyLinkedListContent(&list, &a, &b, &c, &d, &e, &f, nullptr); + + list.RemoveAllMatching(kBetaType, removedList); + VerifyLinkedListContent(&list, &a, &b, &e, nullptr); + VerifyLinkedListContent(&removedList, &f, &d, &c, nullptr); } void TestOwningList(void)