[linked-list] adding LinkedList class (#4357)

This commit adds a linked list module which provides a template
implementation of a singly linked list. The new class is then used in
different core modules (netif (unicast/multicast addresses), UDP6
sockets/receiver, coap resources, etc). This commit also adds a
unit test `test_linked_list` for the linked list module.
This commit is contained in:
Abtin Keshavarzian
2019-12-04 11:03:52 -08:00
committed by Jonathan Hui
parent 15e26f0e64
commit e17c82b0b9
16 changed files with 604 additions and 345 deletions
+1
View File
@@ -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 \
+5 -32
View File
@@ -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)
{
+3 -10
View File
@@ -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<Resource>
{
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<Resource *>(mNext); }
/**
* This method returns a pointer to the Uri-Path.
*
@@ -678,7 +671,7 @@ private:
uint16_t mMessageId;
TimerMilliContext mRetransmissionTimer;
Resource *mResources;
LinkedList<Resource> mResources;
void * mContext;
Interceptor mInterceptor;
+317
View File
@@ -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 <stdio.h>
#include <openthread/error.h>
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<Type>`.
*
* 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 Type> 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<const Type *>(static_cast<const Type *>(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<Type *>(static_cast<Type *>(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<Type *>(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<Type>` class).
*
*/
template <typename Type> 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_
+3 -4
View File
@@ -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);
}
+8 -6
View File
@@ -42,6 +42,7 @@
#include <openthread/instance.h>
#include <openthread/platform/toolchain.h>
#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<Callback>
{
friend class Notifier;
friend class LinkedListEntry<Callback>;
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<Callback> mCallbacks;
ExternalCallback mExternalCallbacks[kMaxExternalHandlers];
};
/**
+23 -50
View File
@@ -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)
{
+5 -3
View File
@@ -43,6 +43,7 @@
#include <openthread/platform/alarm-milli.h>
#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<Timer>
{
friend class TimerScheduler;
friend class LinkedListEntry<Timer>;
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<Timer> mTimerList;
};
/**
+3 -17
View File
@@ -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);
}
+3 -4
View File
@@ -39,6 +39,7 @@
#include <openthread/icmp6.h>
#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<IcmpHandler>
{
friend class Icmp;
@@ -225,8 +226,6 @@ private:
{
mReceiveCallback(mContext, &aMessage, &aMessageInfo, &aIcmp6Header);
}
IcmpHandler *GetNext(void) { return static_cast<IcmpHandler *>(mNext); }
};
/**
@@ -347,7 +346,7 @@ public:
private:
otError HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMessageInfo);
IcmpHandler *mHandlers;
LinkedList<IcmpHandler> mHandlers;
uint16_t mEchoSequence;
otIcmp6EchoMode mEchoMode;
+45 -107
View File
@@ -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<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&kLinkLocalAllNodesMulticastAddress));
mMulticastAddresses.SetHead(static_cast<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&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<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&kLinkLocalAllRoutersMulticastAddress));
mMulticastAddresses.SetHead(static_cast<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&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<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&kLinkLocalAllNodesMulticastAddress));
mMulticastAddresses.SetHead(static_cast<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&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<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED);
exit:
if (error != OT_ERROR_NOT_FOUND)
{
if (mAddressCallback != NULL)
{
mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, false, mAddressCallbackContext);
}
Get<Notifier>().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<Notifier>().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<Notifier>().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<Notifier>().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<Notifier>().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)
{
+10 -32
View File
@@ -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<NetifUnicastAddress>
{
friend class Netif;
@@ -93,29 +94,13 @@ public:
{
return mScopeOverrideValid ? static_cast<uint8_t>(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<const NetifUnicastAddress *>(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<NetifUnicastAddress *>(mNext); }
};
/**
* This class implements an IPv6 network interface multicast address.
*
*/
class NetifMulticastAddress : public otNetifMulticastAddress
class NetifMulticastAddress : public otNetifMulticastAddress, public LinkedListEntry<NetifMulticastAddress>
{
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<Netif>
{
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<NetifUnicastAddress> mUnicastAddresses;
LinkedList<NetifMulticastAddress> mMulticastAddresses;
bool mMulticastPromiscuous;
Netif * mNext;
otIp6AddressCallback mAddressCallback;
void * mAddressCallbackContext;
+14 -68
View File
@@ -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())
{
+7 -12
View File
@@ -38,6 +38,7 @@
#include <openthread/udp.h>
#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<UdpReceiver>
{
friend class Udp;
@@ -80,9 +81,6 @@ public:
}
private:
UdpReceiver *GetNext(void) { return static_cast<UdpReceiver *>(mNext); }
void SetNext(UdpReceiver *aReceiver) { mNext = static_cast<otUdpReceiver *>(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<UdpSocket>
{
friend class Udp;
@@ -202,9 +200,6 @@ public:
SockAddr &GetPeerName(void) { return *static_cast<SockAddr *>(&mPeerName); }
private:
UdpSocket *GetNext(void) { return static_cast<UdpSocket *>(mNext); }
void SetNext(UdpSocket *socket) { mNext = static_cast<otUdpSocket *>(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<UdpReceiver> mReceivers;
LinkedList<UdpSocket> mSockets;
#if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE
void * mUdpForwarderContext;
otUdpForwarder mUdpForwarder;
+5
View File
@@ -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) \
+152
View File
@@ -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 <stdarg.h>
#include "test_platform.h"
#include <openthread/config.h>
#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<Entry>
{
};
// 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;
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<Entry> 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