| /* |
| * 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 "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<Message *>(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<Buffer *>(aMessage)); |
| } |
| |
| Buffer *MessagePool::NewBuffer(Message::Priority aPriority) |
| { |
| Buffer *buffer = nullptr; |
| |
| while (( |
| #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE |
| buffer = static_cast<Buffer *>(Heap::CAlloc(1, sizeof(Buffer))) |
| #elif OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT |
| buffer = static_cast<Buffer *>(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<MeshForwarder>().EvictMessage(aPriority); |
| } |
| |
| uint16_t MessagePool::GetFreeBufferCount(void) const |
| { |
| uint16_t rval; |
| |
| #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE |
| rval = static_cast<uint16_t>(Instance::GetHeap().GetFreeSize() / sizeof(Buffer)); |
| #elif OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT |
| rval = otPlatMessagePoolNumFreeBuffers(&GetInstance()); |
| #else |
| rval = mNumFreeBuffers; |
| #endif |
| |
| return rval; |
| } |
| |
| uint16_t MessagePool::GetTotalBufferCount(void) const |
| { |
| #if OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE |
| return static_cast<uint16_t>(Instance::GetHeap().GetCapacity() / sizeof(Buffer)); |
| #else |
| return OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS; |
| #endif |
| } |
| |
| //--------------------------------------------------------------------------------------------------------------------- |
| // 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<int16_t>(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<uint8_t>(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<uint8_t *>(aBuf); |
| Chunk chunk; |
| |
| GetFirstChunk(aOffset, aLength, chunk); |
| |
| while (chunk.GetLength() > 0) |
| { |
| chunk.CopyBytesTo(bufPtr); |
| bufPtr += chunk.GetLength(); |
| GetNextChunk(aLength, chunk); |
| } |
| |
| return static_cast<uint16_t>(bufPtr - reinterpret_cast<uint8_t *>(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<const uint8_t *>(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<const uint8_t *>(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<Mac::RadioType>(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<uint8_t>(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<Message::Priority>(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 |