mirror of
https://github.com/espressif/openthread.git
synced 2026-08-11 21:27:46 +00:00
[linked-list] add new template {Contains/Find/Remove}Matching() methods (#5174)
This commit adds new methods in `LinkedList` to search in the list for an entry matching a given entry indicator (specified with a template `Indicator` type). To use these methods, users are expected to provide method `bool Matches(const Indicator &aIndicator) const` on the entry `Type` itself (which is then used to match each entry in the list with the indicator). This commit also changes the linked-list methods to use `Find()` internally. Finally it updates the linked-list unit test to cover behavior of the newly added methods.
This commit is contained in:
committed by
Jonathan Hui
parent
a795db710f
commit
5ebd885996
+150
-50
@@ -113,7 +113,7 @@ public:
|
||||
/**
|
||||
* This method returns the entry at the head of the linked list
|
||||
*
|
||||
* @returns Pointer to the entry at the head of the linked list, or nullptr if list is empty.
|
||||
* @returns Pointer to the entry at the head of the linked list, or nullptr if the list is empty.
|
||||
*
|
||||
*/
|
||||
Type *GetHead(void) { return mHead; }
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
/**
|
||||
* This method returns the entry at the head of the linked list.
|
||||
*
|
||||
* @returns Pointer to the entry at the head of the linked list, or nullptr if list is empty.
|
||||
* @returns Pointer to the entry at the head of the linked list, or nullptr if the list is empty.
|
||||
*
|
||||
*/
|
||||
const Type *GetHead(void) const { return mHead; }
|
||||
@@ -179,7 +179,7 @@ public:
|
||||
*
|
||||
* @note This method does not change the popped entry itself, i.e., the popped entry next pointer stays as before.
|
||||
*
|
||||
* @returns The entry that was popped if list is not empty, or nullptr if list is empty.
|
||||
* @returns The entry that was popped if the list is not empty, or nullptr if the list is empty.
|
||||
*
|
||||
*/
|
||||
Type *Pop(void)
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
* @note This method does not change the popped entry itself, i.e., the popped entry next pointer stays as before.
|
||||
*
|
||||
* @param[in] aPrevEntry A pointer to a previous entry. If it is not nullptr the entry after this will be popped,
|
||||
* otherwise (if it is nullptr) the entry at head of the list is popped.
|
||||
* otherwise (if it is nullptr) the entry at the head of the list is popped.
|
||||
*
|
||||
* @returns Pointer to the entry that was popped, or nullptr if there is no entry to pop.
|
||||
*
|
||||
@@ -237,18 +237,31 @@ public:
|
||||
*/
|
||||
bool Contains(const Type &aEntry) const
|
||||
{
|
||||
bool contains = false;
|
||||
const Type *prev;
|
||||
|
||||
for (Type *cur = mHead; cur != nullptr; cur = cur->GetNext())
|
||||
{
|
||||
if (cur == &aEntry)
|
||||
{
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Find(aEntry, prev) == OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
return contains;
|
||||
/**
|
||||
* This template method indicates whether the linked list contains an entry matching a given entry indicator.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @retval TRUE The linked list contains an entry matching @p aIndicator.
|
||||
* @retval FALSE The linked list contains no entry matching @p aIndicator.
|
||||
*
|
||||
*/
|
||||
template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
|
||||
{
|
||||
const Type *prev;
|
||||
|
||||
return FindMatching(aIndicator, prev) != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,7 +292,7 @@ public:
|
||||
/**
|
||||
* This method removes an entry from the linked list.
|
||||
*
|
||||
* @note This method does not change the removed entry @p aEntry itself (it is `const`), i.e., the entry next
|
||||
* @note This method does not change the removed entry @p aEntry itself (it is `const`), i.e., the entry next
|
||||
* pointer of @p aEntry stays as before.
|
||||
*
|
||||
* @param[in] aEntry A reference to an entry to remove.
|
||||
@@ -289,24 +302,73 @@ public:
|
||||
*
|
||||
*/
|
||||
otError Remove(const Type &aEntry)
|
||||
{
|
||||
Type * prev;
|
||||
otError error = Find(aEntry, prev);
|
||||
|
||||
if (error == OT_ERROR_NONE)
|
||||
{
|
||||
PopAfter(prev);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* This template method removes an entry matching a given entry indicator from the linked 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
|
||||
*
|
||||
* @note This method does not change the removed entry itself (which is returned in case of success), i.e., the
|
||||
* entry next pointer stays as before.
|
||||
*
|
||||
*
|
||||
* @param[in] aIndicator An entry indicator to match against entries in the list.
|
||||
*
|
||||
* @returns A pointer to the removed matching entry if one could be found, or nullptr if no matching entry is found.
|
||||
*
|
||||
*/
|
||||
template <typename Indicator> Type *RemoveMatching(const Indicator &aIndicator)
|
||||
{
|
||||
Type *prev;
|
||||
Type *entry = FindMatching(aIndicator, prev);
|
||||
|
||||
if (entry != nullptr)
|
||||
{
|
||||
PopAfter(prev);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method searches within the linked list to find an entry and if found returns a pointer to previous entry.
|
||||
*
|
||||
* @param[in] aEntry A reference to an entry to find.
|
||||
* @param[out] aPrevEntry A pointer to output the previous entry on success (when @p aEntry is found in the list).
|
||||
* @p aPrevEntry is set to nullptr if @p aEntry is the head of the list. Otherwise it is
|
||||
* updated to point to the previous entry before @p aEntry in the list.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The entry was found in the list and @p aPrevEntry was updated successfully.
|
||||
* @retval OT_ERROR_NOT_FOUND The entry was not found in the list.
|
||||
*
|
||||
*/
|
||||
otError Find(const Type &aEntry, const Type *&aPrevEntry) const
|
||||
{
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
|
||||
if (mHead == &aEntry)
|
||||
aPrevEntry = nullptr;
|
||||
|
||||
for (const Type *entry = mHead; entry != nullptr; aPrevEntry = entry, entry = entry->GetNext())
|
||||
{
|
||||
Pop();
|
||||
error = OT_ERROR_NONE;
|
||||
}
|
||||
else if (mHead != nullptr)
|
||||
{
|
||||
for (Type *cur = mHead; cur->GetNext() != nullptr; cur = cur->GetNext())
|
||||
if (entry == &aEntry)
|
||||
{
|
||||
if (cur->GetNext() == &aEntry)
|
||||
{
|
||||
cur->SetNext(cur->GetNext()->GetNext());
|
||||
error = OT_ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
error = OT_ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,43 +379,81 @@ public:
|
||||
* This method searches within the linked list to find an entry and if found returns a pointer to previous entry.
|
||||
*
|
||||
* @param[in] aEntry A reference to an entry to find.
|
||||
* @param[out] aPrevEntry A pointer to output previous entry on success (when @p aEntry is found is the list).
|
||||
* @p aPrevEntry is set to nullptr if the @p aEntry is head of the list. Otherwise it
|
||||
* is updated to point to previous entry before @p aEntry in the list.
|
||||
* @param[out] aPrevEntry A pointer to output the previous entry on success (when @p aEntry is found in the list).
|
||||
* @p aPrevEntry is set to nullptr if @p aEntry is the head of the list. Otherwise it is
|
||||
* updated to point to the previous entry before @p aEntry in the list.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The entry was found in the list and @p aPrevEntry was updated successfully.
|
||||
* @retval OT_ERROR_NOT_FOUND The entry was not found in the list.
|
||||
*
|
||||
*/
|
||||
otError Find(const Type &aEntry, Type *&aPrevEntry) const
|
||||
otError Find(const Type &aEntry, Type *&aPrevEntry)
|
||||
{
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
return const_cast<const LinkedList *>(this)->Find(aEntry, const_cast<const Type *&>(aPrevEntry));
|
||||
}
|
||||
|
||||
if (mHead == &aEntry)
|
||||
/**
|
||||
* This template method searches within the linked list to find an entry matching a given indicator.
|
||||
*
|
||||
* 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 indicator to match with entries in the list..
|
||||
* @param[out] aPrevEntry A pointer to output the previous entry on success (when a match is found in the list).
|
||||
* @p aPrevEntry is set to nullptr if the matching entry is the head of the list. Otherwise
|
||||
* it is updated to point to the previous entry before the matching entry in the list.
|
||||
*
|
||||
* @returns A pointer to the matching entry if one is found, or nullptr if no matching entry was found.
|
||||
*
|
||||
*/
|
||||
template <typename Inidcator> const Type *FindMatching(const Inidcator &aIndicator, const Type *&aPrevEntry) const
|
||||
{
|
||||
const Type *entry;
|
||||
|
||||
aPrevEntry = nullptr;
|
||||
|
||||
for (entry = mHead; entry != nullptr; aPrevEntry = entry, entry = entry->GetNext())
|
||||
{
|
||||
aPrevEntry = nullptr;
|
||||
error = OT_ERROR_NONE;
|
||||
}
|
||||
else if (mHead != nullptr)
|
||||
{
|
||||
for (Type *cur = mHead; cur->GetNext() != nullptr; cur = cur->GetNext())
|
||||
if (entry->Matches(aIndicator))
|
||||
{
|
||||
if (cur->GetNext() == &aEntry)
|
||||
{
|
||||
aPrevEntry = cur;
|
||||
error = OT_ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* This template method searches within the linked list to find an entry matching a given indicator, and if found
|
||||
* returns a pointer to its previous entry in the 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 indicator to match with entries in the list..
|
||||
* @param[out] aPrevEntry A pointer to output the previous entry on success (when a match is found in the list).
|
||||
* @p aPrevEntry is set to nullptr if the matching entry is the head of the list. Otherwise
|
||||
* it is updated to point to the previous entry before the matching entry in the list.
|
||||
*
|
||||
* @returns A pointer to the matching entry if one is found, or nullptr if no matching entry was found.
|
||||
*
|
||||
*/
|
||||
template <typename Inidcator> Type *FindMatching(const Inidcator &aIndicator, Type *&aPrevEntry)
|
||||
{
|
||||
return const_cast<Type *>(
|
||||
const_cast<const LinkedList *>(this)->FindMatching(aIndicator, const_cast<const Type *&>(aPrevEntry)));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the tail of the linked list (i.e., the last entry in the list).
|
||||
*
|
||||
* @returns A pointer to tail entry in the linked list or nullptr if list is empty.
|
||||
* @returns A pointer to the tail entry in the linked list or nullptr if the list is empty.
|
||||
*
|
||||
*/
|
||||
const Type *GetTail(void) const
|
||||
@@ -374,10 +474,10 @@ public:
|
||||
/**
|
||||
* This method returns the tail of the linked list (i.e., the last entry in the list).
|
||||
*
|
||||
* @returns A pointer to tail entry in the linked list or nullptr if list is empty.
|
||||
* @returns A pointer to the tail entry in the linked list or nullptr if the list is empty.
|
||||
*
|
||||
*/
|
||||
Type *GetTail(void) { return const_cast<Type *>(const_cast<const LinkedList<Type> *>(this)->GetTail()); }
|
||||
Type *GetTail(void) { return const_cast<Type *>(const_cast<const LinkedList *>(this)->GetTail()); }
|
||||
|
||||
private:
|
||||
Type *mHead;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test_platform.h"
|
||||
|
||||
@@ -45,29 +46,52 @@ struct EntryBase
|
||||
|
||||
struct Entry : public EntryBase, ot::LinkedListEntry<Entry>
|
||||
{
|
||||
public:
|
||||
Entry(const char *aName, uint16_t aId)
|
||||
: mName(aName)
|
||||
, mId(aId)
|
||||
{
|
||||
}
|
||||
|
||||
const char *GetName(void) const { return mName; }
|
||||
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; }
|
||||
|
||||
private:
|
||||
const char *mName;
|
||||
uint16_t mId;
|
||||
};
|
||||
|
||||
// This function verifies the content of the linked list matches a given list of entries.
|
||||
void VerifyLinkedListContent(const ot::LinkedList<Entry> *aList, ...)
|
||||
{
|
||||
va_list args;
|
||||
Entry * argEntry;
|
||||
Entry * argPrev = nullptr;
|
||||
va_list args;
|
||||
Entry * argEntry;
|
||||
Entry * argPrev = nullptr;
|
||||
const Entry *prev;
|
||||
uint16_t unusedId = 100;
|
||||
|
||||
va_start(args, aList);
|
||||
|
||||
for (const Entry *entry = aList->GetHead(); entry; entry = entry->GetNext())
|
||||
{
|
||||
Entry *prev;
|
||||
|
||||
argEntry = va_arg(args, Entry *);
|
||||
VerifyOrQuit(argEntry != nullptr, "List contains more entries than expected");
|
||||
VerifyOrQuit(argEntry == entry, "List does not contain the same entry");
|
||||
VerifyOrQuit(aList->Contains(*argEntry), "List::Contains() failed");
|
||||
VerifyOrQuit(aList->ContainsMatching(argEntry->GetName()), "List::ContainsMatching() failed");
|
||||
VerifyOrQuit(aList->ContainsMatching(argEntry->GetId()), "List::ContainsMatching() failed");
|
||||
|
||||
SuccessOrQuit(aList->Find(*argEntry, prev), "List::Find() failed");
|
||||
VerifyOrQuit(prev == argPrev, "List::Find() returned prev entry is incorrect");
|
||||
|
||||
VerifyOrQuit(aList->FindMatching(argEntry->GetName(), prev) == argEntry, "List::FindMatching() failed");
|
||||
VerifyOrQuit(prev == argPrev, "List::FindMatching() returned prev entry is incorrect");
|
||||
|
||||
VerifyOrQuit(aList->FindMatching(argEntry->GetId(), prev) == argEntry, "List::FindMatching() failed");
|
||||
VerifyOrQuit(prev == argPrev, "List::FindMatching() returned prev entry is incorrect");
|
||||
|
||||
argPrev = argEntry;
|
||||
}
|
||||
|
||||
@@ -75,11 +99,19 @@ void VerifyLinkedListContent(const ot::LinkedList<Entry> *aList, ...)
|
||||
VerifyOrQuit(argEntry == nullptr, "List contains less entries than expected");
|
||||
|
||||
VerifyOrQuit(aList->GetTail() == argPrev, "List::GetTail() failed");
|
||||
|
||||
VerifyOrQuit(!aList->ContainsMatching("none"), "List::ContainsMatching() succeeded for a missing entry");
|
||||
VerifyOrQuit(!aList->ContainsMatching(unusedId), "List::ContainsMatching() succeeded for a missing entry");
|
||||
|
||||
VerifyOrQuit(aList->FindMatching("none", prev) == nullptr,
|
||||
"LinkedList::FindMatching() succeeded for a missing entry");
|
||||
VerifyOrQuit(aList->FindMatching(unusedId, prev) == nullptr,
|
||||
"LinkedList::FindMatching() succeeded for a missing entry");
|
||||
}
|
||||
|
||||
void TestLinkedList(void)
|
||||
{
|
||||
Entry a, b, c, d, e;
|
||||
Entry a("a", 1), b("b", 2), c("c", 3), d("d", 4), e("e", 5);
|
||||
Entry * prev;
|
||||
ot::LinkedList<Entry> list;
|
||||
|
||||
@@ -117,6 +149,17 @@ void TestLinkedList(void)
|
||||
VerifyLinkedListContent(&list, &d, &c, &b, &a, nullptr);
|
||||
VerifyOrQuit(list.Find(e, prev) == OT_ERROR_NOT_FOUND, "LinkedList::Find() succeeded for a missing entry");
|
||||
|
||||
VerifyOrQuit(list.FindMatching(d.GetName(), prev) == &d, "List::FindMatching() failed");
|
||||
VerifyOrQuit(prev == nullptr, "List::FindMatching() failed");
|
||||
VerifyOrQuit(list.FindMatching(c.GetId(), prev) == &c, "List::FindMatching() failed");
|
||||
VerifyOrQuit(prev == &d, "List::FindMatching() failed");
|
||||
VerifyOrQuit(list.FindMatching(b.GetName(), prev) == &b, "List::FindMatching() failed");
|
||||
VerifyOrQuit(prev == &c, "List::FindMatching() failed");
|
||||
VerifyOrQuit(list.FindMatching(a.GetId(), prev) == &a, "List::FindMatching() failed");
|
||||
VerifyOrQuit(prev == &b, "List::FindMatching() failed");
|
||||
VerifyOrQuit(list.FindMatching(e.GetId(), prev) == nullptr, "List::FindMatching() succeeded for a missing entry");
|
||||
VerifyOrQuit(list.FindMatching(e.GetName(), prev) == nullptr, "List::FindMatching() succeeded for a missing entry");
|
||||
|
||||
list.SetHead(&e);
|
||||
VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
|
||||
|
||||
@@ -161,11 +204,32 @@ void TestLinkedList(void)
|
||||
VerifyOrQuit(list.PopAfter(nullptr) == &a, "LinkedList::PopAfter() failed");
|
||||
VerifyLinkedListContent(&list, &d, &b, &c, nullptr);
|
||||
|
||||
list.Push(e);
|
||||
list.Push(a);
|
||||
VerifyLinkedListContent(&list, &a, &e, &d, &b, &c, nullptr);
|
||||
|
||||
VerifyOrQuit(list.RemoveMatching(a.GetName()) == &a, "LinkedList::RemoveMatching() failed");
|
||||
VerifyLinkedListContent(&list, &e, &d, &b, &c, nullptr);
|
||||
|
||||
VerifyOrQuit(list.RemoveMatching(c.GetId()) == &c, "LinkedList::RemoveMatching() failed");
|
||||
VerifyLinkedListContent(&list, &e, &d, &b, nullptr);
|
||||
|
||||
VerifyOrQuit(list.RemoveMatching(c.GetId()) == nullptr, "LinkedList::RemoveMatching() succeeded for missing entry");
|
||||
VerifyOrQuit(list.RemoveMatching(a.GetName()) == nullptr,
|
||||
"LinkedList::RemoveMatching() succeeded for missing entry");
|
||||
|
||||
VerifyOrQuit(list.RemoveMatching(d.GetId()) == &d, "LinkedList::RemoveMatching() failed");
|
||||
VerifyLinkedListContent(&list, &e, &b, nullptr);
|
||||
|
||||
list.Clear();
|
||||
VerifyOrQuit(list.IsEmpty(), "LinkedList::IsEmpty() failed after Clear()");
|
||||
VerifyOrQuit(list.PopAfter(nullptr) == nullptr, "LinkedList::PopAfter() failed");
|
||||
VerifyLinkedListContent(&list, nullptr);
|
||||
VerifyOrQuit(list.Find(a, prev) == OT_ERROR_NOT_FOUND, "LinkedList::Find() succeeded for a missing entry");
|
||||
VerifyOrQuit(list.FindMatching(b.GetName(), prev) == nullptr, "LinkedList::FindMatching() succeeded when empty");
|
||||
VerifyOrQuit(list.FindMatching(c.GetId(), prev) == nullptr, "LinkedList::FindMatching() succeeded when empty");
|
||||
VerifyOrQuit(list.RemoveMatching(a.GetName()) == nullptr, "LinkedList::RemoveMatching() succeeded when empty");
|
||||
VerifyOrQuit(list.Remove(a) == OT_ERROR_NOT_FOUND, "LinkedList::Remove() succeeded when empty");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
||||
Reference in New Issue
Block a user