[common] adding OwningList (a linked list which owns its entries) (#7181)

This commit adds `OwningList<Type>`, a singly linked list which owns
its entries and frees them upon destruction of the list. This commit
also updates `test_linked_list` to validate the behavior of the new
class.
This commit is contained in:
Abtin Keshavarzian
2021-11-22 21:41:56 -08:00
committed by GitHub
parent 56ca28882a
commit db9e430876
4 changed files with 268 additions and 20 deletions
+1
View File
@@ -412,6 +412,7 @@ openthread_core_files = [
"common/notifier.hpp",
"common/numeric_limits.hpp",
"common/owned_ptr.hpp",
"common/owning_list.hpp",
"common/pool.hpp",
"common/random.hpp",
"common/random_manager.cpp",
+1
View File
@@ -446,6 +446,7 @@ HEADERS_COMMON = \
common/notifier.hpp \
common/numeric_limits.hpp \
common/owned_ptr.hpp \
common/owning_list.hpp \
common/pool.hpp \
common/random.hpp \
common/random_manager.hpp \
+135
View File
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2021, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for a singly linked list which owns its entries and frees them upon destruction of
* the list.
*/
#ifndef OWNING_LIST_HPP_
#define OWNING_LIST_HPP_
#include "openthread-core-config.h"
#include "common/linked_list.hpp"
#include "common/owned_ptr.hpp"
namespace ot {
/**
* This template class represents a singly linked list which owns its entries and frees them upon destruction of the
* list.
*
*/
template <typename Type> class OwningList : public LinkedList<Type>
{
class Iterator;
class ConstIterator;
public:
/**
* This is the default constructor for `OwningList`
*
*/
OwningList(void) = default;
/**
* This is the destructor for `OwningList`.
*
* On destruction, all existing entries in the list are freed.
*
*/
~OwningList(void) { Free(); }
/**
* This method clears the list and frees all existing entries in it.
*
*/
void Free(void)
{
while (!Pop().IsNull())
{
}
}
/**
* This method clears the list and frees all existing entries in it.
*
*/
void Clear(void) { Free(); }
/**
* This method pops an entry from head of the linked list and return an `OwnedPtr` to it.
*
* @note This method does not change the popped entry itself, i.e., the popped entry next pointer stays as before.
*
* @returns An `OwnerPtr` to the entry that was popped (set to null if list of empty).
*
*/
OwnedPtr<Type> Pop(void) { return OwnedPtr<Type>(LinkedList<Type>::Pop()); }
/**
* This method pops an entry after a given previous entry.
*
* @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 the head of the list is popped.
*
* @returns An `OwnerPtr` to the entry that was popped (set to null if there is no entry to pop).
*
*/
OwnedPtr<Type> PopAfter(Type *aPrevEntry) { return OwnedPtr<Type>(LinkedList<Type>::PopAfter(aPrevEntry)); }
/**
* 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 An `OwnerPtr` to the entry that was removed (set to null if there is no matching entry to remove).
*
*/
template <typename Indicator> OwnedPtr<Type> RemoveMatching(const Indicator &aIndicator)
{
return OwnedPtr<Type>(LinkedList<Type>::RemoveMatching(aIndicator));
}
};
} // namespace ot
#endif // OWNING_LIST_HPP_
+131 -20
View File
@@ -36,20 +36,24 @@
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/linked_list.hpp"
#include "common/owning_list.hpp"
#include "test_util.h"
namespace ot {
struct EntryBase
{
EntryBase *mNext;
};
struct Entry : public EntryBase, ot::LinkedListEntry<Entry>
struct Entry : public EntryBase, LinkedListEntry<Entry>
{
public:
Entry(const char *aName, uint16_t aId)
: mName(aName)
, mId(aId)
, mWasFreed(false)
{
}
@@ -57,14 +61,19 @@ 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; }
void Free(void) { mWasFreed = true; }
void ResetTestFlags(void) { mWasFreed = false; }
bool WasFreed(void) const { return mWasFreed; }
private:
const char *mName;
uint16_t mId;
bool mWasFreed;
};
// This function verifies the content of the linked list matches a given list of entries.
void VerifyLinkedListContent(const ot::LinkedList<Entry> *aList, ...)
void VerifyLinkedListContent(const LinkedList<Entry> *aList, ...)
{
va_list args;
Entry * argEntry;
@@ -92,6 +101,8 @@ void VerifyLinkedListContent(const ot::LinkedList<Entry> *aList, ...)
VerifyOrQuit(aList->FindMatching(argEntry->GetId(), prev) == argEntry);
VerifyOrQuit(prev == argPrev, "List::FindMatching() returned prev entry is incorrect");
VerifyOrQuit(!argEntry->WasFreed());
argPrev = argEntry;
}
@@ -109,25 +120,27 @@ void VerifyLinkedListContent(const ot::LinkedList<Entry> *aList, ...)
void TestLinkedList(void)
{
Entry a("a", 1), b("b", 2), c("c", 3), d("d", 4), e("e", 5);
Entry * prev;
ot::LinkedList<Entry> list;
Entry a("a", 1), b("b", 2), c("c", 3), d("d", 4), e("e", 5);
Entry * prev;
LinkedList<Entry> list;
printf("TestLinkedList\n");
VerifyOrQuit(list.IsEmpty(), "failed after init");
VerifyOrQuit(list.GetHead() == nullptr, "failed after init");
VerifyOrQuit(list.Pop() == nullptr, "failed when empty");
VerifyOrQuit(list.Find(a, prev) == ot::kErrorNotFound, "succeeded when empty");
VerifyOrQuit(list.Find(a, prev) == kErrorNotFound, "succeeded when empty");
VerifyLinkedListContent(&list, nullptr);
list.Push(a);
VerifyOrQuit(!list.IsEmpty());
VerifyLinkedListContent(&list, &a, nullptr);
VerifyOrQuit(list.Find(b, prev) == ot::kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.Find(b, prev) == kErrorNotFound, "succeeded for a missing entry");
SuccessOrQuit(list.Add(b));
VerifyLinkedListContent(&list, &b, &a, nullptr);
VerifyOrQuit(list.Find(c, prev) == ot::kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.Find(c, prev) == kErrorNotFound, "succeeded for a missing entry");
list.Push(c);
VerifyLinkedListContent(&list, &c, &b, &a, nullptr);
@@ -138,14 +151,14 @@ void TestLinkedList(void)
SuccessOrQuit(list.Add(e));
VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
VerifyOrQuit(list.Add(a) == ot::kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Add(b) == ot::kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Add(d) == ot::kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Add(e) == ot::kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Add(a) == kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Add(b) == kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Add(d) == kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Add(e) == kErrorAlready, "did not detect duplicate");
VerifyOrQuit(list.Pop() == &e);
VerifyLinkedListContent(&list, &d, &c, &b, &a, nullptr);
VerifyOrQuit(list.Find(e, prev) == ot::kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.Find(e, prev) == kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.FindMatching(d.GetName(), prev) == &d);
VerifyOrQuit(prev == nullptr);
@@ -164,17 +177,17 @@ void TestLinkedList(void)
SuccessOrQuit(list.Remove(c));
VerifyLinkedListContent(&list, &e, &d, &b, &a, nullptr);
VerifyOrQuit(list.Remove(c) == ot::kErrorNotFound);
VerifyOrQuit(list.Remove(c) == kErrorNotFound);
VerifyLinkedListContent(&list, &e, &d, &b, &a, nullptr);
VerifyOrQuit(list.Find(c, prev) == ot::kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.Find(c, prev) == kErrorNotFound, "succeeded for a missing entry");
SuccessOrQuit(list.Remove(e));
VerifyLinkedListContent(&list, &d, &b, &a, nullptr);
VerifyOrQuit(list.Find(e, prev) == ot::kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.Find(e, prev) == kErrorNotFound, "succeeded for a missing entry");
SuccessOrQuit(list.Remove(a));
VerifyLinkedListContent(&list, &d, &b, nullptr);
VerifyOrQuit(list.Find(a, prev) == ot::kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.Find(a, prev) == kErrorNotFound, "succeeded for a missing entry");
list.Push(a);
list.Push(c);
@@ -222,16 +235,114 @@ void TestLinkedList(void)
VerifyOrQuit(list.IsEmpty(), "failed after Clear()");
VerifyOrQuit(list.PopAfter(nullptr) == nullptr);
VerifyLinkedListContent(&list, nullptr);
VerifyOrQuit(list.Find(a, prev) == ot::kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.Find(a, prev) == kErrorNotFound, "succeeded for a missing entry");
VerifyOrQuit(list.FindMatching(b.GetName(), prev) == nullptr, "succeeded when empty");
VerifyOrQuit(list.FindMatching(c.GetId(), prev) == nullptr, "succeeded when empty");
VerifyOrQuit(list.RemoveMatching(a.GetName()) == nullptr, "succeeded when empty");
VerifyOrQuit(list.Remove(a) == ot::kErrorNotFound, "succeeded when empty");
VerifyOrQuit(list.Remove(a) == kErrorNotFound, "succeeded when empty");
}
void TestOwningList(void)
{
Entry a("a", 1), b("b", 2), c("c", 3), d("d", 4), e("e", 5);
OwningList<Entry> list;
OwnedPtr<Entry> ptr;
printf("TestOwningList\n");
VerifyOrQuit(list.IsEmpty());
VerifyOrQuit(list.GetHead() == nullptr);
VerifyOrQuit(list.Pop().IsNull());
list.Free();
VerifyOrQuit(list.IsEmpty());
VerifyOrQuit(list.GetHead() == nullptr);
VerifyOrQuit(list.Pop().IsNull());
// Clear()
list.Push(a);
VerifyLinkedListContent(&list, &a, nullptr);
list.Free();
VerifyOrQuit(list.IsEmpty());
VerifyOrQuit(a.WasFreed());
// Test removing entry without taking back the ownership
a.ResetTestFlags();
list.Push(a);
list.Push(b);
list.Push(c);
list.Push(d);
list.Push(e);
VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
list.Pop();
VerifyLinkedListContent(&list, &d, &c, &b, &a, nullptr);
VerifyOrQuit(e.WasFreed());
list.PopAfter(&c);
VerifyLinkedListContent(&list, &d, &c, &a, nullptr);
VerifyOrQuit(b.WasFreed());
list.RemoveMatching("c");
VerifyLinkedListContent(&list, &d, &a, nullptr);
VerifyOrQuit(c.WasFreed());
list.Free();
VerifyLinkedListContent(&list, nullptr);
VerifyOrQuit(d.WasFreed());
VerifyOrQuit(a.WasFreed());
// Test removing entry and taking ownership
a.ResetTestFlags();
b.ResetTestFlags();
c.ResetTestFlags();
d.ResetTestFlags();
e.ResetTestFlags();
list.Push(a);
list.Push(b);
list.Push(c);
list.Push(d);
list.Push(e);
VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
ptr = list.PopAfter(&a);
VerifyLinkedListContent(&list, &e, &d, &c, &b, &a, nullptr);
VerifyOrQuit(ptr.IsNull());
ptr = list.PopAfter(&e);
VerifyLinkedListContent(&list, &e, &c, &b, &a, nullptr);
VerifyOrQuit(ptr.Get() == &d);
VerifyOrQuit(!d.WasFreed());
ptr = list.Pop();
VerifyLinkedListContent(&list, &c, &b, &a, nullptr);
VerifyOrQuit(ptr.Get() == &e);
VerifyOrQuit(!e.WasFreed());
VerifyOrQuit(d.WasFreed());
ptr = list.RemoveMatching<uint8_t>(1);
VerifyLinkedListContent(&list, &c, &b, nullptr);
VerifyOrQuit(ptr.Get() == &a);
VerifyOrQuit(!a.WasFreed());
VerifyOrQuit(e.WasFreed());
list.Clear();
VerifyOrQuit(c.WasFreed());
VerifyOrQuit(b.WasFreed());
VerifyOrQuit(!a.WasFreed());
a.Free();
VerifyOrQuit(a.WasFreed());
}
} // namespace ot
int main(void)
{
TestLinkedList();
ot::TestLinkedList();
ot::TestOwningList();
printf("All tests passed\n");
return 0;
}