/* * Copyright (c) 2016, 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 implements the message buffer pool and message buffers. */ #include "message.hpp" #include "common/as_core_type.hpp" #include "common/code_utils.hpp" #include "common/debug.hpp" #include "common/heap.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/numeric_limits.hpp" #include "net/checksum.hpp" #include "net/ip6.hpp" #if OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE && OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT #error "OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE conflicts with OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT." #endif #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE && !OPENTHREAD_CONFIG_DTLS_ENABLE #error "OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE is strongly discouraged when OPENTHREAD_CONFIG_DTLS_ENABLE is off." #endif namespace ot { RegisterLogModule("Message"); //--------------------------------------------------------------------------------------------------------------------- // MessagePool MessagePool::MessagePool(Instance &aInstance) : InstanceLocator(aInstance) #if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT && !OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE , mNumFreeBuffers(kNumBuffers) #endif { #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT otPlatMessagePoolInit(&GetInstance(), kNumBuffers, sizeof(Buffer)); #endif } Message *MessagePool::Allocate(Message::Type aType, uint16_t aReserveHeader, const Message::Settings &aSettings) { Error error = kErrorNone; Message *message; VerifyOrExit((message = static_cast(NewBuffer(aSettings.GetPriority()))) != nullptr); memset(message, 0, sizeof(*message)); message->SetMessagePool(this); message->SetType(aType); message->SetReserved(aReserveHeader); message->SetLinkSecurityEnabled(aSettings.IsLinkSecurityEnabled()); SuccessOrExit(error = message->SetPriority(aSettings.GetPriority())); SuccessOrExit(error = message->SetLength(0)); exit: if (error != kErrorNone) { Free(message); message = nullptr; } return message; } void MessagePool::Free(Message *aMessage) { OT_ASSERT(aMessage->Next() == nullptr && aMessage->Prev() == nullptr); FreeBuffers(static_cast(aMessage)); } Buffer *MessagePool::NewBuffer(Message::Priority aPriority) { Buffer *buffer = nullptr; while (( #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE buffer = static_cast(Heap::CAlloc(1, sizeof(Buffer))) #elif OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT buffer = static_cast(otPlatMessagePoolNew(&GetInstance())) #else buffer = mBufferPool.Allocate() #endif ) == nullptr) { SuccessOrExit(ReclaimBuffers(aPriority)); } #if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT && !OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE mNumFreeBuffers--; #endif buffer->SetNextBuffer(nullptr); exit: if (buffer == nullptr) { LogInfo("No available message buffer"); } return buffer; } void MessagePool::FreeBuffers(Buffer *aBuffer) { while (aBuffer != nullptr) { Buffer *next = aBuffer->GetNextBuffer(); #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE Heap::Free(aBuffer); #elif OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT otPlatMessagePoolFree(&GetInstance(), aBuffer); #else mBufferPool.Free(*aBuffer); mNumFreeBuffers++; #endif aBuffer = next; } } Error MessagePool::ReclaimBuffers(Message::Priority aPriority) { return Get().EvictMessage(aPriority); } uint16_t MessagePool::GetFreeBufferCount(void) const { uint16_t rval; #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE rval = static_cast(Instance::GetHeap().GetFreeSize() / sizeof(Buffer)); #else rval = NumericLimits::kMax; #endif #elif OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT rval = otPlatMessagePoolNumFreeBuffers(&GetInstance()); #else rval = mNumFreeBuffers; #endif return rval; } uint16_t MessagePool::GetTotalBufferCount(void) const { uint16_t rval; #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE rval = static_cast(Instance::GetHeap().GetCapacity() / sizeof(Buffer)); #else rval = NumericLimits::kMax; #endif #else rval = OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS; #endif return rval; } //--------------------------------------------------------------------------------------------------------------------- // Message::Settings const otMessageSettings Message::Settings::kDefault = {kWithLinkSecurity, kPriorityNormal}; Message::Settings::Settings(LinkSecurityMode aSecurityMode, Priority aPriority) { mLinkSecurityEnabled = aSecurityMode; mPriority = aPriority; } const Message::Settings &Message::Settings::From(const otMessageSettings *aSettings) { return (aSettings == nullptr) ? GetDefault() : AsCoreType(aSettings); } //--------------------------------------------------------------------------------------------------------------------- // Message::Iterator void Message::Iterator::Advance(void) { mItem = mNext; mNext = NextMessage(mNext); } //--------------------------------------------------------------------------------------------------------------------- // Message Error Message::ResizeMessage(uint16_t aLength) { // This method adds or frees message buffers to meet the // requested length. Error error = kErrorNone; Buffer * curBuffer = this; Buffer * lastBuffer; uint16_t curLength = kHeadBufferDataSize; while (curLength < aLength) { if (curBuffer->GetNextBuffer() == nullptr) { curBuffer->SetNextBuffer(GetMessagePool()->NewBuffer(GetPriority())); VerifyOrExit(curBuffer->GetNextBuffer() != nullptr, error = kErrorNoBufs); } curBuffer = curBuffer->GetNextBuffer(); curLength += kBufferDataSize; } lastBuffer = curBuffer; curBuffer = curBuffer->GetNextBuffer(); lastBuffer->SetNextBuffer(nullptr); GetMessagePool()->FreeBuffers(curBuffer); exit: return error; } void Message::Free(void) { GetMessagePool()->Free(this); } Message *Message::GetNext(void) const { Message *next; Message *tail; if (GetMetadata().mInPriorityQ) { PriorityQueue *priorityQueue = GetPriorityQueue(); VerifyOrExit(priorityQueue != nullptr, next = nullptr); tail = priorityQueue->GetTail(); } else { MessageQueue *messageQueue = GetMessageQueue(); VerifyOrExit(messageQueue != nullptr, next = nullptr); tail = messageQueue->GetTail(); } next = (this == tail) ? nullptr : Next(); exit: return next; } Error Message::SetLength(uint16_t aLength) { Error error = kErrorNone; uint16_t totalLengthRequest = GetReserved() + aLength; VerifyOrExit(totalLengthRequest >= GetReserved(), error = kErrorInvalidArgs); SuccessOrExit(error = ResizeMessage(totalLengthRequest)); GetMetadata().mLength = aLength; // Correct the offset in case shorter length is set. if (GetOffset() > aLength) { SetOffset(aLength); } exit: return error; } uint8_t Message::GetBufferCount(void) const { uint8_t rval = 1; for (const Buffer *curBuffer = GetNextBuffer(); curBuffer; curBuffer = curBuffer->GetNextBuffer()) { rval++; } return rval; } void Message::MoveOffset(int aDelta) { OT_ASSERT(GetOffset() + aDelta <= GetLength()); GetMetadata().mOffset += static_cast(aDelta); OT_ASSERT(GetMetadata().mOffset <= GetLength()); } void Message::SetOffset(uint16_t aOffset) { OT_ASSERT(aOffset <= GetLength()); GetMetadata().mOffset = aOffset; } bool Message::IsSubTypeMle(void) const { bool rval; switch (GetMetadata().mSubType) { case kSubTypeMleGeneral: case kSubTypeMleAnnounce: case kSubTypeMleDiscoverRequest: case kSubTypeMleDiscoverResponse: case kSubTypeMleChildUpdateRequest: case kSubTypeMleDataResponse: case kSubTypeMleChildIdRequest: rval = true; break; default: rval = false; break; } return rval; } Error Message::SetPriority(Priority aPriority) { Error error = kErrorNone; uint8_t priority = static_cast(aPriority); PriorityQueue *priorityQueue; static_assert(kNumPriorities <= 4, "`Metadata::mPriority` as a 2-bit field cannot fit all `Priority` values"); VerifyOrExit(priority < kNumPriorities, error = kErrorInvalidArgs); VerifyOrExit(IsInAQueue(), GetMetadata().mPriority = priority); VerifyOrExit(GetMetadata().mPriority != priority); priorityQueue = GetPriorityQueue(); if (priorityQueue != nullptr) { priorityQueue->Dequeue(*this); } GetMetadata().mPriority = priority; if (priorityQueue != nullptr) { priorityQueue->Enqueue(*this); } exit: return error; } const char *Message::PriorityToString(Priority aPriority) { static const char *const kPriorityStrings[] = { "low", // (0) kPriorityLow "normal", // (1) kPriorityNormal "high", // (2) kPriorityHigh "net", // (3) kPriorityNet }; static_assert(kPriorityLow == 0, "kPriorityLow value is incorrect"); static_assert(kPriorityNormal == 1, "kPriorityNormal value is incorrect"); static_assert(kPriorityHigh == 2, "kPriorityHigh value is incorrect"); static_assert(kPriorityNet == 3, "kPriorityNet value is incorrect"); return kPriorityStrings[aPriority]; } Error Message::AppendBytes(const void *aBuf, uint16_t aLength) { Error error = kErrorNone; uint16_t oldLength = GetLength(); SuccessOrExit(error = SetLength(GetLength() + aLength)); WriteBytes(oldLength, aBuf, aLength); exit: return error; } Error Message::AppendBytesFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength) { Error error = kErrorNone; uint16_t writeOffset = GetLength(); Chunk chunk; VerifyOrExit(aMessage.GetLength() >= aOffset + aLength, error = kErrorParse); SuccessOrExit(error = SetLength(GetLength() + aLength)); aMessage.GetFirstChunk(aOffset, aLength, chunk); while (chunk.GetLength() > 0) { WriteBytes(writeOffset, chunk.GetBytes(), chunk.GetLength()); writeOffset += chunk.GetLength(); aMessage.GetNextChunk(aLength, chunk); } exit: return error; } Error Message::PrependBytes(const void *aBuf, uint16_t aLength) { Error error = kErrorNone; Buffer *newBuffer = nullptr; while (aLength > GetReserved()) { VerifyOrExit((newBuffer = GetMessagePool()->NewBuffer(GetPriority())) != nullptr, error = kErrorNoBufs); newBuffer->SetNextBuffer(GetNextBuffer()); SetNextBuffer(newBuffer); if (GetReserved() < sizeof(mBuffer.mHead.mData)) { // Copy payload from the first buffer. memcpy(newBuffer->mBuffer.mHead.mData + GetReserved(), mBuffer.mHead.mData + GetReserved(), sizeof(mBuffer.mHead.mData) - GetReserved()); } SetReserved(GetReserved() + kBufferDataSize); } SetReserved(GetReserved() - aLength); GetMetadata().mLength += aLength; SetOffset(GetOffset() + aLength); if (aBuf != nullptr) { WriteBytes(0, aBuf, aLength); } exit: return error; } void Message::RemoveHeader(uint16_t aLength) { OT_ASSERT(aLength <= GetMetadata().mLength); GetMetadata().mReserved += aLength; GetMetadata().mLength -= aLength; if (GetMetadata().mOffset > aLength) { GetMetadata().mOffset -= aLength; } else { GetMetadata().mOffset = 0; } } void Message::GetFirstChunk(uint16_t aOffset, uint16_t &aLength, Chunk &aChunk) const { // This method gets the first message chunk (contiguous data // buffer) corresponding to a given offset and length. On exit // `aChunk` is updated such that `aChunk.GetBytes()` gives the // pointer to the start of chunk and `aChunk.GetLength()` gives // its length. The `aLength` is also decreased by the chunk // length. VerifyOrExit(aOffset < GetLength(), aChunk.SetLength(0)); if (aOffset + aLength >= GetLength()) { aLength = GetLength() - aOffset; } aOffset += GetReserved(); aChunk.SetBuffer(this); // Special case for the first buffer if (aOffset < kHeadBufferDataSize) { aChunk.Init(GetFirstData() + aOffset, kHeadBufferDataSize - aOffset); ExitNow(); } aOffset -= kHeadBufferDataSize; // Find the `Buffer` matching the offset while (true) { aChunk.SetBuffer(aChunk.GetBuffer()->GetNextBuffer()); OT_ASSERT(aChunk.GetBuffer() != nullptr); if (aOffset < kBufferDataSize) { aChunk.Init(aChunk.GetBuffer()->GetData() + aOffset, kBufferDataSize - aOffset); ExitNow(); } aOffset -= kBufferDataSize; } exit: if (aChunk.GetLength() > aLength) { aChunk.SetLength(aLength); } aLength -= aChunk.GetLength(); } void Message::GetNextChunk(uint16_t &aLength, Chunk &aChunk) const { // This method gets the next message chunk. On input, the // `aChunk` should be the previous chunk. On exit, it is // updated to provide info about next chunk, and `aLength` // is decreased by the chunk length. If there is no more // chunk, `aChunk.GetLength()` would be zero. VerifyOrExit(aLength > 0, aChunk.SetLength(0)); aChunk.SetBuffer(aChunk.GetBuffer()->GetNextBuffer()); OT_ASSERT(aChunk.GetBuffer() != nullptr); aChunk.Init(aChunk.GetBuffer()->GetData(), kBufferDataSize); if (aChunk.GetLength() > aLength) { aChunk.SetLength(aLength); } aLength -= aChunk.GetLength(); exit: return; } uint16_t Message::ReadBytes(uint16_t aOffset, void *aBuf, uint16_t aLength) const { uint8_t *bufPtr = reinterpret_cast(aBuf); Chunk chunk; GetFirstChunk(aOffset, aLength, chunk); while (chunk.GetLength() > 0) { chunk.CopyBytesTo(bufPtr); bufPtr += chunk.GetLength(); GetNextChunk(aLength, chunk); } return static_cast(bufPtr - reinterpret_cast(aBuf)); } Error Message::Read(uint16_t aOffset, void *aBuf, uint16_t aLength) const { return (ReadBytes(aOffset, aBuf, aLength) == aLength) ? kErrorNone : kErrorParse; } bool Message::CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength, ByteMatcher aMatcher) const { uint16_t bytesToCompare = aLength; const uint8_t *bufPtr = reinterpret_cast(aBuf); Chunk chunk; GetFirstChunk(aOffset, aLength, chunk); while (chunk.GetLength() > 0) { VerifyOrExit(chunk.MatchesBytesIn(bufPtr, aMatcher)); bufPtr += chunk.GetLength(); bytesToCompare -= chunk.GetLength(); GetNextChunk(aLength, chunk); } exit: return (bytesToCompare == 0); } bool Message::CompareBytes(uint16_t aOffset, const Message &aOtherMessage, uint16_t aOtherOffset, uint16_t aLength, ByteMatcher aMatcher) const { uint16_t bytesToCompare = aLength; Chunk chunk; GetFirstChunk(aOffset, aLength, chunk); while (chunk.GetLength() > 0) { VerifyOrExit(aOtherMessage.CompareBytes(aOtherOffset, chunk.GetBytes(), chunk.GetLength(), aMatcher)); aOtherOffset += chunk.GetLength(); bytesToCompare -= chunk.GetLength(); GetNextChunk(aLength, chunk); } exit: return (bytesToCompare == 0); } void Message::WriteBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength) { const uint8_t *bufPtr = reinterpret_cast(aBuf); MutableChunk chunk; OT_ASSERT(aOffset + aLength <= GetLength()); GetFirstChunk(aOffset, aLength, chunk); while (chunk.GetLength() > 0) { memmove(chunk.GetBytes(), bufPtr, chunk.GetLength()); bufPtr += chunk.GetLength(); GetNextChunk(aLength, chunk); } } uint16_t Message::CopyTo(uint16_t aSourceOffset, uint16_t aDestinationOffset, uint16_t aLength, Message &aMessage) const { uint16_t bytesCopied = 0; Chunk chunk; // This implementing can potentially overwrite the data when bytes are // being copied forward within the same message, i.e., source and // destination messages are the same, and source offset is smaller than // the destination offset. We assert not allowing such a use. OT_ASSERT((&aMessage != this) || (aSourceOffset >= aDestinationOffset)); GetFirstChunk(aSourceOffset, aLength, chunk); while (chunk.GetLength() > 0) { aMessage.WriteBytes(aDestinationOffset, chunk.GetBytes(), chunk.GetLength()); aDestinationOffset += chunk.GetLength(); bytesCopied += chunk.GetLength(); GetNextChunk(aLength, chunk); } return bytesCopied; } Message *Message::Clone(uint16_t aLength) const { Error error = kErrorNone; Message *messageCopy; Settings settings(IsLinkSecurityEnabled() ? kWithLinkSecurity : kNoLinkSecurity, GetPriority()); uint16_t offset; messageCopy = GetMessagePool()->Allocate(GetType(), GetReserved(), settings); VerifyOrExit(messageCopy != nullptr, error = kErrorNoBufs); SuccessOrExit(error = messageCopy->SetLength(aLength)); CopyTo(0, 0, aLength, *messageCopy); // Copy selected message information. offset = GetOffset() < aLength ? GetOffset() : aLength; messageCopy->SetOffset(offset); messageCopy->SetSubType(GetSubType()); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE messageCopy->SetTimeSync(IsTimeSync()); #endif exit: FreeAndNullMessageOnError(messageCopy, error); return messageCopy; } bool Message::GetChildMask(uint16_t aChildIndex) const { return GetMetadata().mChildMask.Get(aChildIndex); } void Message::ClearChildMask(uint16_t aChildIndex) { GetMetadata().mChildMask.Set(aChildIndex, false); } void Message::SetChildMask(uint16_t aChildIndex) { GetMetadata().mChildMask.Set(aChildIndex, true); } bool Message::IsChildPending(void) const { return GetMetadata().mChildMask.HasAny(); } void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo) { SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity); SetPanId(aLinkInfo.mPanId); AddRss(aLinkInfo.mRss); #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE AddLqi(aLinkInfo.mLqi); #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq); SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset); #endif #if OPENTHREAD_CONFIG_MULTI_RADIO SetRadioType(static_cast(aLinkInfo.mRadioType)); #endif } bool Message::IsTimeSync(void) const { #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE return GetMetadata().mTimeSync; #else return false; #endif } void Message::SetMessageQueue(MessageQueue *aMessageQueue) { GetMetadata().mQueue = aMessageQueue; GetMetadata().mInPriorityQ = false; } void Message::SetPriorityQueue(PriorityQueue *aPriorityQueue) { GetMetadata().mQueue = aPriorityQueue; GetMetadata().mInPriorityQ = true; } //--------------------------------------------------------------------------------------------------------------------- // MessageQueue void MessageQueue::Enqueue(Message &aMessage, QueuePosition aPosition) { OT_ASSERT(!aMessage.IsInAQueue()); OT_ASSERT((aMessage.Next() == nullptr) && (aMessage.Prev() == nullptr)); aMessage.SetMessageQueue(this); if (GetTail() == nullptr) { aMessage.Next() = &aMessage; aMessage.Prev() = &aMessage; SetTail(&aMessage); } else { Message *head = GetTail()->Next(); aMessage.Next() = head; aMessage.Prev() = GetTail(); head->Prev() = &aMessage; GetTail()->Next() = &aMessage; if (aPosition == kQueuePositionTail) { SetTail(&aMessage); } } } void MessageQueue::Dequeue(Message &aMessage) { OT_ASSERT(aMessage.GetMessageQueue() == this); OT_ASSERT((aMessage.Next() != nullptr) && (aMessage.Prev() != nullptr)); if (&aMessage == GetTail()) { SetTail(GetTail()->Prev()); if (&aMessage == GetTail()) { SetTail(nullptr); } } aMessage.Prev()->Next() = aMessage.Next(); aMessage.Next()->Prev() = aMessage.Prev(); aMessage.Prev() = nullptr; aMessage.Next() = nullptr; aMessage.SetMessageQueue(nullptr); } void MessageQueue::DequeueAndFree(Message &aMessage) { Dequeue(aMessage); aMessage.Free(); } void MessageQueue::DequeueAndFreeAll(void) { Message *message; while ((message = GetHead()) != nullptr) { DequeueAndFree(*message); } } Message::Iterator MessageQueue::begin(void) { return Message::Iterator(GetHead()); } Message::ConstIterator MessageQueue::begin(void) const { return Message::ConstIterator(GetHead()); } void MessageQueue::GetInfo(Info &aInfo) const { for (const Message &message : *this) { aInfo.mNumMessages++; aInfo.mNumBuffers += message.GetBufferCount(); aInfo.mTotalBytes += message.GetLength(); } } //--------------------------------------------------------------------------------------------------------------------- // PriorityQueue const Message *PriorityQueue::FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const { // Find the first non-`nullptr` tail starting from the given priority // level and moving forward (wrapping from priority value // `kNumPriorities` -1 back to 0). const Message *tail = nullptr; uint8_t priority; priority = static_cast(aStartPriorityLevel); do { if (mTails[priority] != nullptr) { tail = mTails[priority]; break; } priority = PrevPriority(priority); } while (priority != aStartPriorityLevel); return tail; } const Message *PriorityQueue::GetHead(void) const { return Message::NextOf(FindFirstNonNullTail(Message::kPriorityLow)); } const Message *PriorityQueue::GetHeadForPriority(Message::Priority aPriority) const { const Message *head; const Message *previousTail; if (mTails[aPriority] != nullptr) { previousTail = FindFirstNonNullTail(static_cast(PrevPriority(aPriority))); OT_ASSERT(previousTail != nullptr); head = previousTail->Next(); } else { head = nullptr; } return head; } const Message *PriorityQueue::GetTail(void) const { return FindFirstNonNullTail(Message::kPriorityLow); } void PriorityQueue::Enqueue(Message &aMessage) { Message::Priority priority; Message * tail; Message * next; OT_ASSERT(!aMessage.IsInAQueue()); aMessage.SetPriorityQueue(this); priority = aMessage.GetPriority(); tail = FindFirstNonNullTail(priority); if (tail != nullptr) { next = tail->Next(); aMessage.Next() = next; aMessage.Prev() = tail; next->Prev() = &aMessage; tail->Next() = &aMessage; } else { aMessage.Next() = &aMessage; aMessage.Prev() = &aMessage; } mTails[priority] = &aMessage; } void PriorityQueue::Dequeue(Message &aMessage) { Message::Priority priority; Message * tail; OT_ASSERT(aMessage.GetPriorityQueue() == this); priority = aMessage.GetPriority(); tail = mTails[priority]; if (&aMessage == tail) { tail = tail->Prev(); if ((&aMessage == tail) || (tail->GetPriority() != priority)) { tail = nullptr; } mTails[priority] = tail; } aMessage.Next()->Prev() = aMessage.Prev(); aMessage.Prev()->Next() = aMessage.Next(); aMessage.Next() = nullptr; aMessage.Prev() = nullptr; aMessage.SetPriorityQueue(nullptr); } void PriorityQueue::DequeueAndFree(Message &aMessage) { Dequeue(aMessage); aMessage.Free(); } void PriorityQueue::DequeueAndFreeAll(void) { Message *message; while ((message = GetHead()) != nullptr) { DequeueAndFree(*message); } } Message::Iterator PriorityQueue::begin(void) { return Message::Iterator(GetHead()); } Message::ConstIterator PriorityQueue::begin(void) const { return Message::ConstIterator(GetHead()); } void PriorityQueue::GetInfo(Info &aInfo) const { for (const Message &message : *this) { aInfo.mNumMessages++; aInfo.mNumBuffers += message.GetBufferCount(); aInfo.mTotalBytes += message.GetLength(); } } } // namespace ot #endif // OPENTHREAD_MTD || OPENTHREAD_FTD