diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 19907aca6..2ec7ae824 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -310,6 +310,7 @@ HEADERS_COMMON = \ common/encoding.hpp \ common/extension.hpp \ common/instance.hpp \ + common/linked_list.hpp \ common/locator.hpp \ common/locator-getters.hpp \ common/logging.hpp \ diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index 3e5a9b29e..e84ffabb7 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -49,7 +49,7 @@ namespace Coap { CoapBase::CoapBase(Instance &aInstance, Sender aSender) : InstanceLocator(aInstance) , mRetransmissionTimer(aInstance, &Coap::HandleRetransmissionTimer, this) - , mResources(NULL) + , mResources() , mContext(NULL) , mInterceptor(NULL) , mResponsesQueue(aInstance) @@ -81,40 +81,13 @@ void CoapBase::ClearRequestsAndResponses(void) otError CoapBase::AddResource(Resource &aResource) { - otError error = OT_ERROR_NONE; - - for (Resource *cur = mResources; cur; cur = cur->GetNext()) - { - VerifyOrExit(cur != &aResource, error = OT_ERROR_ALREADY); - } - - aResource.mNext = mResources; - mResources = &aResource; - -exit: - return error; + return mResources.Add(aResource); } void CoapBase::RemoveResource(Resource &aResource) { - if (mResources == &aResource) - { - mResources = aResource.GetNext(); - } - else - { - for (Resource *cur = mResources; cur; cur = cur->GetNext()) - { - if (cur->mNext == &aResource) - { - cur->mNext = aResource.mNext; - ExitNow(); - } - } - } - -exit: - aResource.mNext = NULL; + mResources.Remove(aResource); + aResource.SetNext(NULL); } void CoapBase::SetDefaultHandler(otCoapRequestHandler aHandler, void *aContext) @@ -611,7 +584,7 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo curUriPath[0] = '\0'; - for (const Resource *resource = mResources; resource; resource = resource->GetNext()) + for (const Resource *resource = mResources.GetHead(); resource; resource = resource->GetNext()) { if (strcmp(resource->mUriPath, uriPath) == 0) { diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index a660962a7..dc2f96a2e 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -35,6 +35,7 @@ #include "coap/coap_message.hpp" #include "common/debug.hpp" +#include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/message.hpp" #include "common/timer.hpp" @@ -175,7 +176,7 @@ private: * This class implements CoAP resource handling. * */ -class Resource : public otCoapResource +class Resource : public otCoapResource, public LinkedListEntry { friend class CoapBase; @@ -200,14 +201,6 @@ public: mNext = NULL; } - /** - * This method returns a pointer to the next resource. - * - * @returns A pointer to the next resource. - * - */ - Resource *GetNext(void) const { return static_cast(mNext); } - /** * This method returns a pointer to the Uri-Path. * @@ -678,7 +671,7 @@ private: uint16_t mMessageId; TimerMilliContext mRetransmissionTimer; - Resource *mResources; + LinkedList mResources; void * mContext; Interceptor mInterceptor; diff --git a/src/core/common/linked_list.hpp b/src/core/common/linked_list.hpp new file mode 100644 index 000000000..ff694821b --- /dev/null +++ b/src/core/common/linked_list.hpp @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2019, 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 generic singly linked list. + */ + +#ifndef LINKED_LIST_HPP_ +#define LINKED_LIST_HPP_ + +#include "openthread-core-config.h" + +#include +#include + +namespace ot { + +/** + * @addtogroup core-linked-list + * + * @brief + * This module includes definitions for OpenThread Singly Linked List. + * + * @{ + * + */ + +/** + * This template class represents a linked list entry. + * + * This class provides methods to `GetNext()` and `SetNext()` in the linked list entry. + * + * Users of this class should follow CRTP-style inheritance, i.e., the `Type` class itself should publicly inherit + * from `LinkedListEntry`. + * + * The template type `Type` should contain a `mNext` member variable. The `mNext` should be of a type that can be + * down-casted to `Type` itself. + * + */ +template class LinkedListEntry +{ +public: + /** + * This method gets the next entry in the linked list. + * + * @returns A pointer to the next entry in the linked list or NULL if at the end of the list. + * + */ + const Type *GetNext(void) const { return static_cast(static_cast(this)->mNext); } + + /** + * This method gets the next entry in the linked list. + * + * @returns A pointer to the next entry in the linked list or NULL if at the end of the list. + * + */ + Type *GetNext(void) { return static_cast(static_cast(this)->mNext); } + + /** + * This method sets the next pointer on the entry. + * + * @param[in] aNext A pointer to the next entry. + * + */ + void SetNext(Type *aNext) { static_cast(this)->mNext = aNext; } +}; + +/** + * This template class represents a singly linked list. + * + * The template type `Type` should provide `GetNext()` and `SetNext()` methods (which can be realized by `Type` + * inheriting from `LinkedListEntry` class). + * + */ +template class LinkedList +{ +public: + /** + * This constructor initializes the linked list. + * + */ + LinkedList(void) + : mHead(NULL) + { + } + + /** + * 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 NULL if list is empty. + * + */ + Type *GetHead(void) { return mHead; } + + /** + * 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 NULL if list is empty. + * + */ + const Type *GetHead(void) const { return mHead; } + + /** + * This method sets the head of the linked list to a given entry. + * + * @param[in] aHead A pointer to an entry to set as the head of the linked list. + * + */ + void SetHead(Type *aHead) { mHead = aHead; } + + /** + * This method clears the linked list. + * + */ + void Clear(void) { mHead = NULL; } + + /** + * This method indicates whether the linked list is empty or not. + * + * @retval TRUE If the linked list is empty. + * @retval FALSE If the linked list is not empty. + * + */ + bool IsEmpty(void) const { return (mHead == NULL); } + + /** + * This method pushes an entry at the head of the linked list. + * + * @param[in] aEntry A reference to an entry to push at the head of linked list. + * + */ + void Push(Type &aEntry) + { + aEntry.SetNext(mHead); + mHead = &aEntry; + } + + /** + * This method pushes an entry after a given previous existing entry in the linked list. + * + * @param[in] aEntry A reference to an entry to push into the list. + * @param[in] aPrevEntry A reference to a previous entry (new entry @p aEntry will be pushed after this). + * + */ + void PushAfter(Type &aEntry, Type &aPrevEntry) + { + aEntry.SetNext(aPrevEntry.GetNext()); + aPrevEntry.SetNext(&aEntry); + } + + /** + * This method pops an entry from head of the linked list. + * + * @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 NULL if list is empty. + * + */ + Type *Pop(void) + { + Type *entry = mHead; + + if (mHead != NULL) + { + mHead = mHead->GetNext(); + } + + return entry; + } + + /** + * 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 reference to n previous entry (the entry after this will be popped). + + * @returns The entry that was popped if list is not empty, or NULL if there is no entry after the given one. + * + */ + Type *PopAfter(Type &aPrevEntry) + { + Type *entry = aPrevEntry.GetNext(); + + if (entry != NULL) + { + aPrevEntry.SetNext(entry->GetNext()); + } + + return entry; + } + + /** + * This method indicates whether the linked list contains a given entry. + * + * @param[in] aEntry A reference to an entry. + * + * @retval TRUE The linked list contains @p aEntry. + * @retval FALSE The linked list does not contain @p aEntry. + * + */ + bool Contains(Type &aEntry) const + { + bool contains = false; + + for (Type *cur = mHead; cur != NULL; cur = cur->GetNext()) + { + if (cur == &aEntry) + { + contains = true; + break; + } + } + + return contains; + } + + /** + * This method adds an entry (at the head of the linked list) if it is not already in the list. + * + * @param[in] aEntry A reference to an entry to add. + * + * @retval OT_ERROR_NONE The entry was successfully added at the head of the list. + * @retval OT_ERROR_ALREADY The entry is already in the list. + * + */ + otError Add(Type &aEntry) + { + otError error = OT_ERROR_NONE; + + if (Contains(aEntry)) + { + error = OT_ERROR_ALREADY; + } + else + { + Push(aEntry); + } + + return error; + } + + /** + * 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 + * pointer of @p aEntry stays as before. + * + * @param[in] aEntry A reference to an entry to remove. + * + * @retval OT_ERROR_NONE The entry was successfully removed from the list. + * @retval OT_ERROR_NOT_FOUND Could not find the entry in the list. + * + */ + otError Remove(const Type &aEntry) + { + otError error = OT_ERROR_NOT_FOUND; + + if (mHead == &aEntry) + { + Pop(); + error = OT_ERROR_NONE; + } + else if (mHead != NULL) + { + for (Type *cur = mHead; cur->GetNext() != NULL; cur = cur->GetNext()) + { + if (cur->GetNext() == &aEntry) + { + cur->SetNext(cur->GetNext()->GetNext()); + error = OT_ERROR_NONE; + break; + } + } + } + + return error; + } + +private: + Type *mHead; +}; + +/** + * @} + * + */ + +} // namespace ot + +#endif // LINKED_LIST_HPP_ diff --git a/src/core/common/notifier.cpp b/src/core/common/notifier.cpp index aa7190456..749f8cf09 100644 --- a/src/core/common/notifier.cpp +++ b/src/core/common/notifier.cpp @@ -54,7 +54,7 @@ Notifier::Notifier(Instance &aInstance) , mFlagsToSignal(0) , mSignaledFlags(0) , mTask(aInstance, &Notifier::HandleStateChanged, this) - , mCallbacks(NULL) + , mCallbacks() { for (unsigned int i = 0; i < kMaxExternalHandlers; i++) { @@ -65,8 +65,7 @@ Notifier::Notifier(Instance &aInstance) void Notifier::RegisterCallback(Callback &aCallback) { - aCallback.mNext = mCallbacks; - mCallbacks = &aCallback; + mCallbacks.Push(aCallback); } otError Notifier::RegisterCallback(otStateChangedCallback aCallback, void *aContext) @@ -151,7 +150,7 @@ void Notifier::HandleStateChanged(void) LogChangedFlags(flags); - for (Callback *callback = mCallbacks; callback != NULL; callback = callback->mNext) + for (Callback *callback = mCallbacks.GetHead(); callback != NULL; callback = callback->GetNext()) { callback->Invoke(flags); } diff --git a/src/core/common/notifier.hpp b/src/core/common/notifier.hpp index 8c8ea6605..65e98fda9 100644 --- a/src/core/common/notifier.hpp +++ b/src/core/common/notifier.hpp @@ -42,6 +42,7 @@ #include #include +#include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/tasklet.hpp" @@ -79,9 +80,10 @@ public: * This class defines a `Notifier` callback instance. * */ - class Callback : public OwnerLocator + class Callback : public OwnerLocator, public LinkedListEntry { friend class Notifier; + friend class LinkedListEntry; public: /** @@ -232,11 +234,11 @@ private: void LogChangedFlags(otChangedFlags aFlags) const; const char *FlagToString(otChangedFlags aFlag) const; - otChangedFlags mFlagsToSignal; - otChangedFlags mSignaledFlags; - Tasklet mTask; - Callback * mCallbacks; - ExternalCallback mExternalCallbacks[kMaxExternalHandlers]; + otChangedFlags mFlagsToSignal; + otChangedFlags mSignaledFlags; + Tasklet mTask; + LinkedList mCallbacks; + ExternalCallback mExternalCallbacks[kMaxExternalHandlers]; }; /** diff --git a/src/core/common/timer.cpp b/src/core/common/timer.cpp index 24925e0ec..3dda5830d 100644 --- a/src/core/common/timer.cpp +++ b/src/core/common/timer.cpp @@ -100,73 +100,45 @@ void TimerMilli::Stop(void) void TimerScheduler::Add(Timer &aTimer, const AlarmApi &aAlarmApi) { + Timer *prev = NULL; + Time now(aAlarmApi.AlarmGetNow()); + Remove(aTimer, aAlarmApi); - if (mHead == NULL) + for (Timer *cur = mTimerList.GetHead(); cur; prev = cur, cur = cur->GetNext()) { - mHead = &aTimer; - aTimer.mNext = NULL; + if (aTimer.DoesFireBefore(*cur, now)) + { + break; + } + } + + if (prev == NULL) + { + mTimerList.Push(aTimer); SetAlarm(aAlarmApi); } else { - Timer *prev = NULL; - Timer *cur; - - for (cur = mHead; cur; cur = cur->mNext) - { - Time now(aAlarmApi.AlarmGetNow()); - - if (aTimer.DoesFireBefore(*cur, now)) - { - if (prev) - { - aTimer.mNext = cur; - prev->mNext = &aTimer; - } - else - { - aTimer.mNext = mHead; - mHead = &aTimer; - SetAlarm(aAlarmApi); - } - - break; - } - - prev = cur; - } - - if (cur == NULL) - { - prev->mNext = &aTimer; - aTimer.mNext = NULL; - } + mTimerList.PushAfter(aTimer, *prev); } } void TimerScheduler::Remove(Timer &aTimer, const AlarmApi &aAlarmApi) { - VerifyOrExit(aTimer.mNext != &aTimer); + VerifyOrExit(aTimer.IsRunning()); - if (mHead == &aTimer) + if (mTimerList.GetHead() == &aTimer) { - mHead = aTimer.mNext; + mTimerList.Pop(); SetAlarm(aAlarmApi); } else { - for (Timer *cur = mHead; cur; cur = cur->mNext) - { - if (cur->mNext == &aTimer) - { - cur->mNext = aTimer.mNext; - break; - } - } + mTimerList.Remove(aTimer); } - aTimer.mNext = &aTimer; + aTimer.SetNext(&aTimer); exit: return; @@ -174,16 +146,17 @@ exit: void TimerScheduler::SetAlarm(const AlarmApi &aAlarmApi) { - if (mHead == NULL) + if (mTimerList.IsEmpty()) { aAlarmApi.AlarmStop(&GetInstance()); } else { + Timer * timer = mTimerList.GetHead(); Time now(aAlarmApi.AlarmGetNow()); uint32_t remaining; - remaining = (now < mHead->mFireTime) ? (mHead->mFireTime - now) : 0; + remaining = (now < timer->mFireTime) ? (timer->mFireTime - now) : 0; aAlarmApi.AlarmStartAt(&GetInstance(), now.GetValue(), remaining); } @@ -191,7 +164,7 @@ void TimerScheduler::SetAlarm(const AlarmApi &aAlarmApi) void TimerScheduler::ProcessTimers(const AlarmApi &aAlarmApi) { - Timer *timer = mHead; + Timer *timer = mTimerList.GetHead(); if (timer) { diff --git a/src/core/common/timer.hpp b/src/core/common/timer.hpp index c07881356..1b899a98d 100644 --- a/src/core/common/timer.hpp +++ b/src/core/common/timer.hpp @@ -43,6 +43,7 @@ #include #include "common/debug.hpp" +#include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/tasklet.hpp" #include "common/time.hpp" @@ -65,9 +66,10 @@ class TimerMilliScheduler; * This class implements a timer. * */ -class Timer : public InstanceLocator, public OwnerLocator +class Timer : public InstanceLocator, public OwnerLocator, public LinkedListEntry { friend class TimerScheduler; + friend class LinkedListEntry; public: /** @@ -273,7 +275,7 @@ protected: */ explicit TimerScheduler(Instance &aInstance) : InstanceLocator(aInstance) - , mHead(NULL) + , mTimerList() { } @@ -311,7 +313,7 @@ protected: */ void SetAlarm(const AlarmApi &aAlarmApi); - Timer *mHead; + LinkedList mTimerList; }; /** diff --git a/src/core/net/icmp6.cpp b/src/core/net/icmp6.cpp index e4a981b42..9d9b48b9d 100644 --- a/src/core/net/icmp6.cpp +++ b/src/core/net/icmp6.cpp @@ -50,7 +50,7 @@ namespace Ip6 { Icmp::Icmp(Instance &aInstance) : InstanceLocator(aInstance) - , mHandlers(NULL) + , mHandlers() , mEchoSequence(1) , mEchoMode(OT_ICMP6_ECHO_HANDLER_ALL) { @@ -63,21 +63,7 @@ Message *Icmp::NewMessage(uint16_t aReserved) otError Icmp::RegisterHandler(IcmpHandler &aHandler) { - otError error = OT_ERROR_NONE; - - for (IcmpHandler *cur = mHandlers; cur; cur = cur->GetNext()) - { - if (cur == &aHandler) - { - ExitNow(error = OT_ERROR_ALREADY); - } - } - - aHandler.mNext = mHandlers; - mHandlers = &aHandler; - -exit: - return error; + return mHandlers.Add(aHandler); } otError Icmp::SendEchoRequest(Message &aMessage, const MessageInfo &aMessageInfo, uint16_t aIdentifier) @@ -163,7 +149,7 @@ otError Icmp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo) aMessage.MoveOffset(sizeof(icmp6Header)); - for (IcmpHandler *handler = mHandlers; handler; handler = handler->GetNext()) + for (IcmpHandler *handler = mHandlers.GetHead(); handler; handler = handler->GetNext()) { handler->HandleReceiveMessage(aMessage, aMessageInfo, icmp6Header); } diff --git a/src/core/net/icmp6.hpp b/src/core/net/icmp6.hpp index e52d9f736..100af7f20 100644 --- a/src/core/net/icmp6.hpp +++ b/src/core/net/icmp6.hpp @@ -39,6 +39,7 @@ #include #include "common/encoding.hpp" +#include "common/linked_list.hpp" #include "common/locator.hpp" #include "net/ip6_headers.hpp" @@ -201,7 +202,7 @@ public: * This class implements ICMPv6 message handlers. * */ -class IcmpHandler : public otIcmp6Handler +class IcmpHandler : public otIcmp6Handler, public LinkedListEntry { friend class Icmp; @@ -225,8 +226,6 @@ private: { mReceiveCallback(mContext, &aMessage, &aMessageInfo, &aIcmp6Header); } - - IcmpHandler *GetNext(void) { return static_cast(mNext); } }; /** @@ -347,7 +346,7 @@ public: private: otError HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMessageInfo); - IcmpHandler *mHandlers; + LinkedList mHandlers; uint16_t mEchoSequence; otIcmp6EchoMode mEchoMode; diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index a4696c11e..d50352715 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -82,8 +82,8 @@ const otNetifMulticastAddress Netif::kLinkLocalAllRoutersMulticastAddress = { Netif::Netif(Instance &aInstance) : InstanceLocator(aInstance) - , mUnicastAddresses(NULL) - , mMulticastAddresses(NULL) + , mUnicastAddresses() + , mMulticastAddresses() , mMulticastPromiscuous(false) , mNext(NULL) , mAddressCallback(NULL) @@ -107,7 +107,7 @@ bool Netif::IsMulticastSubscribed(const Address &aAddress) const { bool rval = false; - for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) + for (const NetifMulticastAddress *cur = mMulticastAddresses.GetHead(); cur; cur = cur->GetNext()) { if (cur->GetAddress() == aAddress) { @@ -121,10 +121,10 @@ exit: void Netif::SubscribeAllNodesMulticast(void) { - assert(mMulticastAddresses == NULL); + assert(mMulticastAddresses.IsEmpty()); - mMulticastAddresses = static_cast( - const_cast(&kLinkLocalAllNodesMulticastAddress)); + mMulticastAddresses.SetHead(static_cast( + const_cast(&kLinkLocalAllNodesMulticastAddress))); if (mAddressCallback != NULL) { @@ -140,9 +140,9 @@ void Netif::SubscribeAllNodesMulticast(void) void Netif::UnsubscribeAllNodesMulticast(void) { - assert(mMulticastAddresses == NULL || mMulticastAddresses == &kLinkLocalAllNodesMulticastAddress); + assert(mMulticastAddresses.IsEmpty() || mMulticastAddresses.GetHead() == &kLinkLocalAllNodesMulticastAddress); - mMulticastAddresses = NULL; + mMulticastAddresses.SetHead(NULL); if (mAddressCallback != NULL) { @@ -160,14 +160,14 @@ otError Netif::SubscribeAllRoutersMulticast(void) { otError error = OT_ERROR_NONE; - if (mMulticastAddresses == &kLinkLocalAllNodesMulticastAddress) + if (mMulticastAddresses.GetHead() == &kLinkLocalAllNodesMulticastAddress) { - mMulticastAddresses = static_cast( - const_cast(&kLinkLocalAllRoutersMulticastAddress)); + mMulticastAddresses.SetHead(static_cast( + const_cast(&kLinkLocalAllRoutersMulticastAddress))); } else { - for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) + for (NetifMulticastAddress *cur = mMulticastAddresses.GetHead(); cur; cur = cur->GetNext()) { if (cur == &kLinkLocalAllRoutersMulticastAddress) { @@ -201,14 +201,14 @@ otError Netif::UnsubscribeAllRoutersMulticast(void) { otError error = OT_ERROR_NONE; - if (mMulticastAddresses == &kLinkLocalAllRoutersMulticastAddress) + if (mMulticastAddresses.GetHead() == &kLinkLocalAllRoutersMulticastAddress) { - mMulticastAddresses = static_cast( - const_cast(&kLinkLocalAllNodesMulticastAddress)); + mMulticastAddresses.SetHead(static_cast( + const_cast(&kLinkLocalAllNodesMulticastAddress))); ExitNow(); } - for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) + for (NetifMulticastAddress *cur = mMulticastAddresses.GetHead(); cur; cur = cur->GetNext()) { if (cur->mNext == &kLinkLocalAllRoutersMulticastAddress) { @@ -240,18 +240,9 @@ exit: otError Netif::SubscribeMulticast(NetifMulticastAddress &aAddress) { - otError error = OT_ERROR_NONE; + otError error; - for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) - { - if (cur == &aAddress) - { - ExitNow(error = OT_ERROR_ALREADY); - } - } - - aAddress.mNext = mMulticastAddresses; - mMulticastAddresses = &aAddress; + SuccessOrExit(error = mMulticastAddresses.Add(aAddress)); if (mAddressCallback != NULL) { @@ -266,39 +257,18 @@ exit: otError Netif::UnsubscribeMulticast(const NetifMulticastAddress &aAddress) { - otError error = OT_ERROR_NONE; + otError error; - if (mMulticastAddresses == &aAddress) + SuccessOrExit(error = mMulticastAddresses.Remove(aAddress)); + + if (mAddressCallback != NULL) { - mMulticastAddresses = mMulticastAddresses->GetNext(); - ExitNow(); - } - else if (mMulticastAddresses != NULL) - { - for (NetifMulticastAddress *cur = mMulticastAddresses; cur->GetNext(); cur = cur->GetNext()) - { - if (cur->mNext == &aAddress) - { - cur->mNext = aAddress.mNext; - ExitNow(); - } - } + mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, false, mAddressCallbackContext); } - ExitNow(error = OT_ERROR_NOT_FOUND); + Get().Signal(OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED); exit: - - if (error != OT_ERROR_NOT_FOUND) - { - if (mAddressCallback != NULL) - { - mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, false, mAddressCallbackContext); - } - - Get().Signal(OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED); - } - return error; } @@ -332,7 +302,7 @@ otError Netif::SubscribeExternalMulticast(const Address &aAddress) otError error = OT_ERROR_NONE; NetifMulticastAddress *entry; - VerifyOrExit(mMulticastAddresses != NULL, error = OT_ERROR_INVALID_STATE); + VerifyOrExit(!mMulticastAddresses.IsEmpty(), error = OT_ERROR_INVALID_STATE); if (IsMulticastSubscribed(aAddress)) { @@ -352,9 +322,8 @@ otError Netif::SubscribeExternalMulticast(const Address &aAddress) VerifyOrExit(entry < OT_ARRAY_END(mExtMulticastAddresses), error = OT_ERROR_NO_BUFS); // Copy the address into the available entry and add it to linked-list. - entry->mAddress = aAddress; - entry->mNext = mMulticastAddresses; - mMulticastAddresses = entry; + entry->mAddress = aAddress; + mMulticastAddresses.Push(*entry); Get().Signal(OT_CHANGED_IP6_MULTICAST_SUBSCRIBED); exit: @@ -367,7 +336,7 @@ otError Netif::UnsubscribeExternalMulticast(const Address &aAddress) NetifMulticastAddress *entry; NetifMulticastAddress *last = NULL; - for (entry = mMulticastAddresses; entry; entry = entry->GetNext()) + for (entry = mMulticastAddresses.GetHead(); entry; entry = entry->GetNext()) { if (entry->GetAddress() == aAddress) { @@ -376,11 +345,11 @@ otError Netif::UnsubscribeExternalMulticast(const Address &aAddress) if (last) { - last->mNext = entry->GetNext(); + mMulticastAddresses.PopAfter(*last); } else { - mMulticastAddresses = entry->GetNext(); + mMulticastAddresses.Pop(); } break; @@ -421,18 +390,9 @@ void Netif::SetAddressCallback(otIp6AddressCallback aCallback, void *aCallbackCo otError Netif::AddUnicastAddress(NetifUnicastAddress &aAddress) { - otError error = OT_ERROR_NONE; + otError error; - for (NetifUnicastAddress *cur = mUnicastAddresses; cur; cur = cur->GetNext()) - { - if (cur == &aAddress) - { - ExitNow(error = OT_ERROR_ALREADY); - } - } - - aAddress.mNext = mUnicastAddresses; - mUnicastAddresses = &aAddress; + SuccessOrExit(error = mUnicastAddresses.Add(aAddress)); if (mAddressCallback != NULL) { @@ -447,39 +407,18 @@ exit: otError Netif::RemoveUnicastAddress(const NetifUnicastAddress &aAddress) { - otError error = OT_ERROR_NONE; + otError error; - if (mUnicastAddresses == &aAddress) + SuccessOrExit(error = mUnicastAddresses.Remove(aAddress)); + + if (mAddressCallback != NULL) { - mUnicastAddresses = mUnicastAddresses->GetNext(); - ExitNow(); - } - else if (mUnicastAddresses != NULL) - { - for (NetifUnicastAddress *cur = mUnicastAddresses; cur->GetNext(); cur = cur->GetNext()) - { - if (cur->mNext == &aAddress) - { - cur->mNext = aAddress.mNext; - ExitNow(); - } - } + mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, false, mAddressCallbackContext); } - ExitNow(error = OT_ERROR_NOT_FOUND); + Get().Signal(aAddress.mRloc ? OT_CHANGED_THREAD_RLOC_REMOVED : OT_CHANGED_IP6_ADDRESS_REMOVED); exit: - - if (error != OT_ERROR_NOT_FOUND) - { - if (mAddressCallback != NULL) - { - mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, false, mAddressCallbackContext); - } - - Get().Signal(aAddress.mRloc ? OT_CHANGED_THREAD_RLOC_REMOVED : OT_CHANGED_IP6_ADDRESS_REMOVED); - } - return error; } @@ -490,7 +429,7 @@ otError Netif::AddExternalUnicastAddress(const NetifUnicastAddress &aAddress) VerifyOrExit(!aAddress.GetAddress().IsLinkLocal(), error = OT_ERROR_INVALID_ARGS); - for (entry = mUnicastAddresses; entry; entry = entry->GetNext()) + for (entry = mUnicastAddresses.GetHead(); entry; entry = entry->GetNext()) { if (entry->GetAddress() == aAddress.GetAddress()) { @@ -517,9 +456,8 @@ otError Netif::AddExternalUnicastAddress(const NetifUnicastAddress &aAddress) VerifyOrExit(entry < OT_ARRAY_END(mExtUnicastAddresses), error = OT_ERROR_NO_BUFS); // Copy the new address into the available entry and insert it in linked-list. - *entry = aAddress; - entry->mNext = mUnicastAddresses; - mUnicastAddresses = entry; + *entry = aAddress; + mUnicastAddresses.Push(*entry); Get().Signal(OT_CHANGED_IP6_ADDRESS_ADDED); @@ -533,7 +471,7 @@ otError Netif::RemoveExternalUnicastAddress(const Address &aAddress) NetifUnicastAddress *entry; NetifUnicastAddress *last = NULL; - for (entry = mUnicastAddresses; entry; entry = entry->GetNext()) + for (entry = mUnicastAddresses.GetHead(); entry; entry = entry->GetNext()) { if (entry->GetAddress() == aAddress) { @@ -542,11 +480,11 @@ otError Netif::RemoveExternalUnicastAddress(const Address &aAddress) if (last) { - last->mNext = entry->mNext; + mUnicastAddresses.PopAfter(*last); } else { - mUnicastAddresses = entry->GetNext(); + mUnicastAddresses.Pop(); } break; @@ -582,7 +520,7 @@ bool Netif::IsUnicastAddress(const Address &aAddress) const { bool rval = false; - for (const NetifUnicastAddress *cur = mUnicastAddresses; cur; cur = cur->GetNext()) + for (const NetifUnicastAddress *cur = mUnicastAddresses.GetHead(); cur; cur = cur->GetNext()) { if (cur->GetAddress() == aAddress) { diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index 5a25f8c70..b9cd863ea 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -36,6 +36,7 @@ #include "openthread-core-config.h" +#include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/message.hpp" #include "common/tasklet.hpp" @@ -62,7 +63,7 @@ class Ip6; * This class implements an IPv6 network interface unicast address. * */ -class NetifUnicastAddress : public otNetifAddress +class NetifUnicastAddress : public otNetifAddress, public LinkedListEntry { friend class Netif; @@ -93,29 +94,13 @@ public: { return mScopeOverrideValid ? static_cast(mScopeOverride) : GetAddress().GetScope(); } - - /** - * This method returns the next unicast address assigned to the interface. - * - * @returns A pointer to the next unicast address. - * - */ - const NetifUnicastAddress *GetNext(void) const { return static_cast(mNext); } - - /** - * This method returns the next unicast address assigned to the interface. - * - * @returns A pointer to the next unicast address. - * - */ - NetifUnicastAddress *GetNext(void) { return static_cast(mNext); } }; /** * This class implements an IPv6 network interface multicast address. * */ -class NetifMulticastAddress : public otNetifMulticastAddress +class NetifMulticastAddress : public otNetifMulticastAddress, public LinkedListEntry { friend class Netif; @@ -160,7 +145,7 @@ public: * This class implements an IPv6 network interface. * */ -class Netif : public InstanceLocator +class Netif : public InstanceLocator, public LinkedListEntry { friend class Ip6; @@ -173,13 +158,6 @@ public: */ Netif(Instance &aInstance); - /** - * This method returns the next network interface in the list. - * - * @returns A pointer to the next network interface. - */ - Netif *GetNext(void) const { return mNext; } - /** * This method registers a callback to notify internal IPv6 address changes. * @@ -195,7 +173,7 @@ public: * @returns A pointer to the list of unicast addresses. * */ - const NetifUnicastAddress *GetUnicastAddresses(void) const { return mUnicastAddresses; } + const NetifUnicastAddress *GetUnicastAddresses(void) const { return mUnicastAddresses.GetHead(); } /** * This method adds a unicast address to the network interface. @@ -295,7 +273,7 @@ public: * @returns A pointer to the list of multicast addresses. * */ - const NetifMulticastAddress *GetMulticastAddresses(void) const { return mMulticastAddresses; } + const NetifMulticastAddress *GetMulticastAddresses(void) const { return mMulticastAddresses.GetHead(); } /** * This method subscribes the network interface to a multicast address. @@ -402,10 +380,10 @@ private: kMulticastPrefixLength = 128, ///< Multicast prefix length used to notify internal address changes. }; - NetifUnicastAddress * mUnicastAddresses; - NetifMulticastAddress *mMulticastAddresses; - bool mMulticastPromiscuous; - Netif * mNext; + LinkedList mUnicastAddresses; + LinkedList mMulticastAddresses; + bool mMulticastPromiscuous; + Netif * mNext; otIp6AddressCallback mAddressCallback; void * mAddressCallbackContext; diff --git a/src/core/net/udp6.cpp b/src/core/net/udp6.cpp index c059e7f3b..874ef00af 100644 --- a/src/core/net/udp6.cpp +++ b/src/core/net/udp6.cpp @@ -205,8 +205,8 @@ exit: Udp::Udp(Instance &aInstance) : InstanceLocator(aInstance) , mEphemeralPort(kDynamicPortMin) - , mReceivers(NULL) - , mSockets(NULL) + , mReceivers() + , mSockets() #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE , mUdpForwarderContext(NULL) , mUdpForwarder(NULL) @@ -216,86 +216,32 @@ Udp::Udp(Instance &aInstance) otError Udp::AddReceiver(UdpReceiver &aReceiver) { - otError error = OT_ERROR_NONE; - - for (UdpReceiver *cur = mReceivers; cur; cur = cur->GetNext()) - { - if (cur == &aReceiver) - { - ExitNow(error = OT_ERROR_ALREADY); - } - } - - aReceiver.SetNext(mReceivers); - mReceivers = &aReceiver; - -exit: - return error; + return mReceivers.Add(aReceiver); } otError Udp::RemoveReceiver(UdpReceiver &aReceiver) { - otError error = OT_ERROR_NOT_FOUND; + otError error; - if (mReceivers == &aReceiver) - { - mReceivers = mReceivers->GetNext(); - aReceiver.SetNext(NULL); - error = OT_ERROR_NONE; - } - else - { - for (UdpReceiver *handler = mReceivers; handler; handler = handler->GetNext()) - { - if (handler->GetNext() == &aReceiver) - { - handler->SetNext(aReceiver.GetNext()); - aReceiver.SetNext(NULL); - error = OT_ERROR_NONE; - break; - } - } - } + SuccessOrExit(error = mReceivers.Remove(aReceiver)); + aReceiver.SetNext(NULL); +exit: return error; } void Udp::AddSocket(UdpSocket &aSocket) { - for (UdpSocket *cur = mSockets; cur; cur = cur->GetNext()) - { - if (cur == &aSocket) - { - ExitNow(); - } - } - - aSocket.SetNext(mSockets); - mSockets = &aSocket; - -exit: - return; + mSockets.Add(aSocket); } void Udp::RemoveSocket(UdpSocket &aSocket) { - if (mSockets == &aSocket) - { - mSockets = mSockets->GetNext(); - } - else - { - for (UdpSocket *socket = mSockets; socket; socket = socket->GetNext()) - { - if (socket->GetNext() == &aSocket) - { - socket->SetNext(aSocket.GetNext()); - break; - } - } - } - + SuccessOrExit(mSockets.Remove(aSocket)); aSocket.SetNext(NULL); + +exit: + return; } uint16_t Udp::GetEphemeralPort(void) @@ -382,7 +328,7 @@ otError Udp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo) VerifyOrExit(IsMle(GetInstance(), aMessageInfo.mSockPort)); #endif - for (UdpReceiver *receiver = mReceivers; receiver; receiver = receiver->GetNext()) + for (UdpReceiver *receiver = mReceivers.GetHead(); receiver; receiver = receiver->GetNext()) { VerifyOrExit(!receiver->HandleMessage(aMessage, aMessageInfo)); } @@ -396,7 +342,7 @@ exit: void Udp::HandlePayload(Message &aMessage, MessageInfo &aMessageInfo) { // find socket - for (UdpSocket *socket = mSockets; socket; socket = socket->GetNext()) + for (UdpSocket *socket = mSockets.GetHead(); socket; socket = socket->GetNext()) { if (socket->GetSockName().mPort != aMessageInfo.GetSockPort()) { diff --git a/src/core/net/udp6.hpp b/src/core/net/udp6.hpp index 51402bffc..fee48bb77 100644 --- a/src/core/net/udp6.hpp +++ b/src/core/net/udp6.hpp @@ -38,6 +38,7 @@ #include +#include "common/linked_list.hpp" #include "common/locator.hpp" #include "net/ip6_headers.hpp" @@ -60,7 +61,7 @@ class Udp; * This class implements a UDP receiver. * */ -class UdpReceiver : public otUdpReceiver +class UdpReceiver : public otUdpReceiver, public LinkedListEntry { friend class Udp; @@ -80,9 +81,6 @@ public: } private: - UdpReceiver *GetNext(void) { return static_cast(mNext); } - void SetNext(UdpReceiver *aReceiver) { mNext = static_cast(aReceiver); } - bool HandleMessage(Message &aMessage, const MessageInfo &aMessageInfo) { return mHandler(mContext, &aMessage, &aMessageInfo); @@ -93,7 +91,7 @@ private: * This class implements a UDP/IPv6 socket. * */ -class UdpSocket : public otUdpSocket, public InstanceLocator +class UdpSocket : public otUdpSocket, public InstanceLocator, public LinkedListEntry { friend class Udp; @@ -202,9 +200,6 @@ public: SockAddr &GetPeerName(void) { return *static_cast(&mPeerName); } private: - UdpSocket *GetNext(void) { return static_cast(mNext); } - void SetNext(UdpSocket *socket) { mNext = static_cast(socket); } - void HandleUdpReceive(Message &aMessage, const MessageInfo &aMessageInfo) { mHandler(mContext, &aMessage, &aMessageInfo); @@ -329,7 +324,7 @@ public: void UpdateChecksum(Message &aMessage, uint16_t aChecksum); #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE - otUdpSocket *GetUdpSockets(void) { return mSockets; } + otUdpSocket *GetUdpSockets(void) { return mSockets.GetHead(); } #endif #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE @@ -354,9 +349,9 @@ private: kDynamicPortMax = 65535, ///< Service Name and Transport Protocol Port Number Registry }; - uint16_t mEphemeralPort; - UdpReceiver *mReceivers; - UdpSocket * mSockets; + uint16_t mEphemeralPort; + LinkedList mReceivers; + LinkedList mSockets; #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE void * mUdpForwarderContext; otUdpForwarder mUdpForwarder; diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 8162b3905..9f6b209da 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -111,6 +111,7 @@ check_PROGRAMS += \ test-hmac-sha256 \ test-ip6-address \ test-link-quality \ + test-linked-list \ test-lowpan \ test-mac-frame \ test-message \ @@ -185,6 +186,9 @@ test_ip6_address_SOURCES = test_platform.cpp test_ip6_address.cpp test_link_quality_LDADD = $(COMMON_LDADD) test_link_quality_SOURCES = test_platform.cpp test_link_quality.cpp +test_linked_list_LDADD = $(COMMON_LDADD) +test_linked_list_SOURCES = test_platform.cpp test_linked_list.cpp + test_lowpan_LDADD = $(COMMON_LDADD) test_lowpan_SOURCES = test_platform.cpp test_lowpan.cpp test_util.cpp @@ -243,6 +247,7 @@ PRETTY_FILES = \ $(test_heap_SOURCES) \ $(test_hmac_sha256_SOURCES) \ $(test_link_quality_SOURCES) \ + $(test_linked_list_SOURCES) \ $(test_lowpan_SOURCES) \ $(test_mac_frame_SOURCES) \ $(test_message_queue_SOURCES) \ diff --git a/tests/unit/test_linked_list.cpp b/tests/unit/test_linked_list.cpp new file mode 100644 index 000000000..5b915735d --- /dev/null +++ b/tests/unit/test_linked_list.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2019, 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. + */ + +#include + +#include "test_platform.h" + +#include + +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/linked_list.hpp" + +#include "test_util.h" + +struct EntryBase +{ + EntryBase *mNext; +}; + +struct Entry : public EntryBase, ot::LinkedListEntry +{ +}; + +// This function verifies the content of the linked list matches a given list of entries. +void VerifyLinkedListContent(const ot::LinkedList &aList, ...) +{ + va_list args; + Entry * argEntry; + + va_start(args, aList); + + for (const Entry *entry = aList.GetHead(); entry; entry = entry->GetNext()) + { + argEntry = va_arg(args, Entry *); + VerifyOrQuit(argEntry != NULL, "List contains more entries than expected"); + VerifyOrQuit(argEntry == entry, "List does not contain the same entry"); + VerifyOrQuit(aList.Contains(*argEntry), "List::Contains() failed"); + } + + argEntry = va_arg(args, Entry *); + VerifyOrQuit(argEntry == NULL, "List contains less entries than expected"); +} + +void TestLinkedList(void) +{ + Entry a, b, c, d, e; + ot::LinkedList list; + + VerifyOrQuit(list.IsEmpty(), "LinkedList::IsEmpty() failed after init"); + VerifyOrQuit(list.GetHead() == NULL, "LinkedList::GetHead() failed after init"); + VerifyOrQuit(list.Pop() == NULL, "LinkedList::Pop() failed when empty"); + + VerifyLinkedListContent(list, NULL); + + list.Push(a); + VerifyOrQuit(!list.IsEmpty(), "LinkedList::IsEmpty() failed"); + VerifyLinkedListContent(list, &a, NULL); + + SuccessOrQuit(list.Add(b), "LinkedList::Add() failed"); + VerifyLinkedListContent(list, &b, &a, NULL); + + list.Push(c); + VerifyLinkedListContent(list, &c, &b, &a, NULL); + + SuccessOrQuit(list.Add(d), "LinkedList::Add() failed"); + VerifyLinkedListContent(list, &d, &c, &b, &a, NULL); + + SuccessOrQuit(list.Add(e), "LinkedList::Add() failed"); + VerifyLinkedListContent(list, &e, &d, &c, &b, &a, NULL); + + VerifyOrQuit(list.Add(a) == OT_ERROR_ALREADY, "LinkedList::Add() did not detect duplicate"); + VerifyOrQuit(list.Add(b) == OT_ERROR_ALREADY, "LinkedList::Add() did not detect duplicate"); + VerifyOrQuit(list.Add(d) == OT_ERROR_ALREADY, "LinkedList::Add() did not detect duplicate"); + VerifyOrQuit(list.Add(e) == OT_ERROR_ALREADY, "LinkedList::Add() did not detect duplicate"); + + VerifyOrQuit(list.Pop() == &e, "LinkedList::Pop() failed"); + VerifyLinkedListContent(list, &d, &c, &b, &a, NULL); + + list.SetHead(&e); + VerifyLinkedListContent(list, &e, &d, &c, &b, &a, NULL); + + SuccessOrQuit(list.Remove(c), "LinkedList::Remove() failed"); + VerifyLinkedListContent(list, &e, &d, &b, &a, NULL); + + VerifyOrQuit(list.Remove(c) == OT_ERROR_NOT_FOUND, "LinkedList::Remove() failed"); + VerifyLinkedListContent(list, &e, &d, &b, &a, NULL); + + SuccessOrQuit(list.Remove(e), "LinkedList::Remove() failed"); + VerifyLinkedListContent(list, &d, &b, &a, NULL); + + SuccessOrQuit(list.Remove(a), "LinkedList::Remove() failed"); + VerifyLinkedListContent(list, &d, &b, NULL); + + list.Push(a); + list.Push(c); + list.Push(e); + VerifyLinkedListContent(list, &e, &c, &a, &d, &b, NULL); + + VerifyOrQuit(list.PopAfter(a) == &d, "LinkedList::PopAfter() failed"); + VerifyLinkedListContent(list, &e, &c, &a, &b, NULL); + + VerifyOrQuit(list.PopAfter(b) == NULL, "LinkedList::PopAfter() failed"); + VerifyLinkedListContent(list, &e, &c, &a, &b, NULL); + + VerifyOrQuit(list.PopAfter(e) == &c, "LinkedList::PopAfter() failed"); + VerifyLinkedListContent(list, &e, &a, &b, NULL); + + list.PushAfter(c, b); + VerifyLinkedListContent(list, &e, &a, &b, &c, NULL); + + list.PushAfter(d, a); + VerifyLinkedListContent(list, &e, &a, &d, &b, &c, NULL); + + list.Clear(); + VerifyOrQuit(list.IsEmpty(), "LinkedList::IsEmpty() failed after Clear()"); + VerifyLinkedListContent(list, NULL); +} + +#ifdef ENABLE_TEST_MAIN +int main(void) +{ + TestLinkedList(); + printf("All tests passed\n"); + return 0; +} +#endif