[message] add clone methods to MessageAllocator (#12704)

This commit adds `CloneMessage()`, `CloneMessageWithoutFooter()`,
and `CloneMessageWithout<Footer>()` methods to the `MessageAllocator`
class. These methods simplify creating copies of messages by
automatically applying the correct `kReservedHeader` size. It also
updates existing code in `CoapBase`, `Dns::Client`, `Sntp::Client`,
and `Mle` to utilize these new methods.

Additionally, this commit updates the `Clone()` method in `Message`
to be a template method, accepting a `CloneMode` to specify whether
the cloned message should retain the reserved header or have no
reserved header. The documentation for the clone methods has also
been updated to clarify which message fields are copied during the
cloning process.
This commit is contained in:
Abtin Keshavarzian
2026-03-23 15:42:16 -05:00
committed by GitHub
parent bb06821cea
commit 78ddbf7845
16 changed files with 133 additions and 63 deletions
+1 -1
View File
@@ -122,7 +122,7 @@ int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint
return aLength;
}
otMessage *otMessageClone(const otMessage *aMessage) { return AsCoreType(aMessage).Clone(); }
otMessage *otMessageClone(const otMessage *aMessage) { return AsCoreType(aMessage).Clone<kNoReservedHeader>(); }
void otMessageQueueInit(otMessageQueue *aQueue) { AsCoreType(aQueue).Clear(); }
+5 -5
View File
@@ -1018,7 +1018,7 @@ Error CoapBase::CacheLastBlockResponse(Message *aResponse)
FreeLastBlockResponse();
mLastResponse = AsCoapMessagePtr(aResponse->Clone());
mLastResponse = AsCoapMessagePtr(aResponse->Clone<kNoReservedHeader>());
VerifyOrExit(mLastResponse != nullptr, error = kErrorNoBufs);
exit:
@@ -1549,7 +1549,7 @@ Error CoapBase::PendingRequests::Add(const Msg &aTxMsg,
cloneLength = aTxMsg.IsConfirmable() ? aTxMsg.mMessage.GetLength() : aTxMsg.GetHeaderSize();
aRequest.mMessage = AsCoapMessagePtr(aTxMsg.mMessage.Clone(cloneLength));
aRequest.mMessage = AsCoapMessagePtr(aTxMsg.mMessage.Clone<kNoReservedHeader>(cloneLength));
VerifyOrExit(aRequest.HasMessage(), error = kErrorNoBufs);
SuccessOrExit(error = aRequest.AppendMetadataToMessage());
@@ -1688,7 +1688,7 @@ void CoapBase::PendingRequests::RetransmitRequest(const Request &aRequest)
Message *clone;
Ip6::MessageInfo messageInfo;
clone = AsCoapMessagePtr(aRequest.mMessage->Clone(aRequest.mMessage->GetLength() - sizeof(Request::Metadata)));
clone = mCoapBase.CloneMessageWithout<Request::Metadata>(aRequest.GetMessage());
VerifyOrExit(clone != nullptr, error = kErrorNoBufs);
aRequest.mMetadata.CopyInfoTo(messageInfo);
@@ -1802,7 +1802,7 @@ Error CoapBase::ResponseCache::SendCachedResponse(const Msg &aRxMsg, CoapBase &a
VerifyOrExit(match != nullptr, error = kErrorNotFound);
response = AsCoapMessagePtr(match->Clone(match->GetLength() - sizeof(ResponseMetadata)));
response = aCoapBase.CloneMessageWithout<ResponseMetadata>(*match);
VerifyOrExit(response != nullptr, error = kErrorNoBufs);
error = aCoapBase.Transmit(*response, aRxMsg.mMessageInfo);
@@ -1848,7 +1848,7 @@ void CoapBase::ResponseCache::Add(const Msg &aTxMsg, uint32_t aExchangeLifetime)
MaintainCacheSize();
responseClone = AsCoapMessagePtr(aTxMsg.mMessage.Clone());
responseClone = AsCoapMessagePtr(aTxMsg.mMessage.Clone<kNoReservedHeader>());
VerifyOrExit(responseClone != nullptr);
metadata.mExpireTime = TimerMilli::GetNow() + aExchangeLifetime;
+11 -4
View File
@@ -785,10 +785,6 @@ void Message::WriteBytesFromMessage(uint16_t aWriteOffset,
}
}
Message *Message::Clone(void) const { return Clone(GetLength()); }
Message *Message::Clone(uint16_t aLength) const { return Clone(aLength, GetReserved()); }
Message *Message::Clone(uint16_t aLength, uint16_t aReserveHeader) const
{
Error error = kErrorNone;
@@ -824,6 +820,17 @@ exit:
return clone;
}
template <> Message *Message::Clone<kNoReservedHeader>(void) const { return Clone(GetLength(), 0); }
template <> Message *Message::Clone<kSameReservedHeader>(void) const { return Clone(GetLength(), GetReserved()); }
template <> Message *Message::Clone<kNoReservedHeader>(uint16_t aLength) const { return Clone(aLength, 0); }
template <> Message *Message::Clone<kSameReservedHeader>(uint16_t aLength) const
{
return Clone(aLength, GetReserved());
}
Error Message::GetLinkInfo(ThreadLinkInfo &aLinkInfo) const
{
Error error = kErrorNone;
+42 -26
View File
@@ -156,6 +156,15 @@ enum LinkSecurityMode : bool
kWithLinkSecurity = true, ///< Link security enabled.
};
/**
* Represents the clone mode indicating how the reserved header should be configured on the cloned message.
*/
enum CloneMode : uint8_t
{
kNoReservedHeader, ///< The clone message will have no reserved header.
kSameReservedHeader ///< The clone message will have the same reserved header size as the original `Message`.
};
/**
* Represents a Message buffer.
*/
@@ -1099,35 +1108,12 @@ public:
WriteBytes(aOffset, aData.GetBytes(), aData.GetLength());
}
/**
* Creates a copy of the message.
*
* It allocates the new message from the same message pool as the original one and copies the entire payload. The
* `Type`, `SubType`, `LinkSecurity`, `Offset`, and `Priority` fields on the cloned message are also
* copied from the original one.
*
* @returns A pointer to the message or `nullptr` if insufficient message buffers are available.
*/
Message *Clone(void) const;
/**
* Creates a copy of the message.
*
* It allocates the new message from the same message pool as the original one and copies @p aLength octets
* of the payload. The `Type`, `SubType`, `LinkSecurity`, `Offset`, and `Priority` fields on the cloned message
* are also copied from the original one.
*
* @param[in] aLength Number of message bytes to copy.
*
* @returns A pointer to the message or nullptr if insufficient message buffers are available.
*/
Message *Clone(uint16_t aLength) const;
/**
* Creates a copy of the message using a given configuration.
*
* It allocates the new message from the same message pool as the original one. The `Type`, `SubType`,
* `LinkSecurity`, `Offset`, and `Priority` fields on the cloned message are copied from the original one.
* The `Type`, `SubType`, `LinkSecurity`, `Offset`, `Priority`, `LoopbackToHostAllowed`, `Origin`, `Timestamp`,
* `MeshDest`, `PanId`, `Channel`, `RssAverager`, `LqiAverager`, and `TimeSync` fields on the cloned message are
* also copied from the original one.
*
* @param[in] aLength Number of message bytes to copy.
* @param[in] aReserveHeader Number of header bytes to reserve in the new cloned message.
@@ -1136,6 +1122,30 @@ public:
*/
Message *Clone(uint16_t aLength, uint16_t aReserveHeader) const;
/**
* Creates a copy of the message.
*
* @tparam kMode Specifies the clone mode (whether to keep the same reserved header size or have none).
*
* See the non-templated `Clone()` method for details on which message fields are also copied.
*
* @returns A pointer to the message or `nullptr` if insufficient message buffers are available.
*/
template <CloneMode kMode> Message *Clone(void) const;
/**
* Creates a copy of the message.
*
* @tparam kMode Specifies the clone mode (whether to keep the same reserved header size or have none).
*
* See the non-templated `Clone()` method for details on which message fields are also copied.
*
* @param[in] aLength Number of message bytes to copy.
*
* @returns A pointer to the message or `nullptr` if insufficient message buffers are available.
*/
template <CloneMode kMode> Message *Clone(uint16_t aLength) const;
/**
* Returns the datagram tag used for 6LoWPAN fragmentation or the identification used for IPv6
* fragmentation.
@@ -1996,6 +2006,12 @@ private:
uint16_t mMaxAllocated;
};
// Declare specializations of `Message::Clone<CloneMode>()` (implemented in `message.cpp`).
template <> Message *Message::Clone<kNoReservedHeader>(void) const;
template <> Message *Message::Clone<kSameReservedHeader>(void) const;
template <> Message *Message::Clone<kNoReservedHeader>(uint16_t aLength) const;
template <> Message *Message::Clone<kSameReservedHeader>(uint16_t aLength) const;
/**
* @}
*/
+48
View File
@@ -37,6 +37,7 @@
#include "openthread-core-config.h"
#include "common/message.hpp"
#include "common/type_traits.hpp"
#include "net/ip6_headers.hpp"
namespace ot {
@@ -126,6 +127,53 @@ public:
*/
MessageType *NewNetPriorityMessage(void) { return NewMessage(Message::Settings(Message::kPriorityNet)); }
/**
* Creates a copy of a given message.
*
* The new message will be allocated with the `kReservedHeader` reserved header size.
*
* @param[in] aMessage The message to copy.
*
* @returns A pointer to the message or `nullptr` if no buffers are available.
*/
MessageType *CloneMessage(const MessageType &aMessage)
{
return AsMessageType(aMessage.Clone(aMessage.GetLength(), kReservedHeader));
}
/**
* Creates a copy of a given message without a footer.
*
* The new message will be allocated with the `kReservedHeader` reserved header size.
*
* @param[in] aMessage The message to copy.
* @param[in] aFooterLength The size of the footer to omit from the cloned message.
*
* @returns A pointer to the message or `nullptr` if no buffers are available.
*/
MessageType *CloneMessageWithoutFooter(const MessageType &aMessage, uint16_t aFooterLength)
{
return AsMessageType(aMessage.Clone(aMessage.GetLength() - aFooterLength, kReservedHeader));
}
/**
* Creates a copy of a given message without a given footer type.
*
* The new message will be allocated with the `kReservedHeader` reserved header size.
*
* @tparam FooterType The footer type to omit from the cloned message.
*
* @param[in] aMessage The message to copy.
*
* @returns A pointer to the message or `nullptr` if no buffers are available.
*/
template <typename FooterType> MessageType *CloneMessageWithout(const MessageType &aMessage)
{
static_assert(!TypeTraits::IsPointer<FooterType>::kValue, "FooterType must not be a pointer");
return AsMessageType(aMessage.Clone(aMessage.GetLength() - sizeof(FooterType), kReservedHeader));
}
protected:
MessageAllocator(void) = default;
+3 -3
View File
@@ -1584,8 +1584,8 @@ void Client::SaveQueryResponse(Query &aQuery, const Message &aResponseMessage)
info.ReadFrom(aQuery);
VerifyOrExit(info.mSavedResponse == nullptr);
// If `Clone()` fails we let retry or timeout handle the error.
info.mSavedResponse = aResponseMessage.Clone();
// If clone fails we let retry or timeout handle the error.
info.mSavedResponse = aResponseMessage.Clone<kNoReservedHeader>();
UpdateQuery(aQuery, info);
@@ -1823,7 +1823,7 @@ Error Client::ReplaceWithSeparateSrvTxtQueries(Query &aQuery)
RecordServerAsLimitedToSingleQuestion(info.mConfig.GetServerSockAddr().GetAddress());
secondQuery = aQuery.Clone();
secondQuery = mSocket.CloneMessage(aQuery);
VerifyOrExit(secondQuery != nullptr);
info.mQueryType = kServiceQueryTxt;
+2 -2
View File
@@ -184,7 +184,7 @@ Error Ip6::PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Heade
if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() &&
Get<ChildTable>().HasSleepyChildWithAddress(aHeader.GetDestination()))
{
Message *messageCopy = aMessage.Clone();
Message *messageCopy = aMessage.Clone<kSameReservedHeader>();
if (messageCopy != nullptr)
{
@@ -855,7 +855,7 @@ Error Ip6::TakeOrCopyMessagePtr(OwnedPtr<Message> &aTargetPtr,
break;
case kCopyMessageToUse:
aTargetPtr.Reset(aMessagePtr->Clone());
aTargetPtr.Reset(aMessagePtr->Clone<kNoReservedHeader>());
break;
}
+2 -2
View File
@@ -342,7 +342,7 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque
#endif
VerifyOrExit(DetermineMaxRetransmissions() > 0);
VerifyOrExit((messageCopy = aMessage.Clone()) != nullptr, error = kErrorNoBufs);
VerifyOrExit((messageCopy = aMessage.Clone<kSameReservedHeader>()) != nullptr, error = kErrorNoBufs);
if (aMessage.IsOriginThreadNetif())
{
@@ -413,7 +413,7 @@ void Mpl::HandleRetransmissionTimer(void)
nextTime.UpdateIfEarlier(metadata.mTransmissionTime);
messageCopy = message.Clone();
messageCopy = message.Clone<kSameReservedHeader>();
}
else
{
+4 -6
View File
@@ -128,7 +128,7 @@ Message *Client::CopyAndEnqueueMessage(const Message &aMessage, const QueryMetad
Message *messageCopy = nullptr;
// Create a message copy for further retransmissions.
VerifyOrExit((messageCopy = aMessage.Clone()) != nullptr, error = kErrorNoBufs);
VerifyOrExit((messageCopy = aMessage.Clone<kNoReservedHeader>()) != nullptr, error = kErrorNoBufs);
// Append the copy with retransmission data and add it to the queue.
SuccessOrExit(error = aQueryMetadata.AppendTo(*messageCopy));
@@ -160,13 +160,11 @@ Error Client::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInf
void Client::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Error error;
Message *messageCopy = nullptr;
Message *messageCopy;
// Create a message copy for lower layers.
VerifyOrExit((messageCopy = aMessage.Clone(aMessage.GetLength() - sizeof(QueryMetadata))) != nullptr,
error = kErrorNoBufs);
messageCopy = mSocket.CloneMessageWithout<QueryMetadata>(aMessage);
VerifyOrExit(messageCopy != nullptr, error = kErrorNoBufs);
// Send the copy.
SuccessOrExit(error = SendMessage(*messageCopy, aMessageInfo));
exit:
+2 -1
View File
@@ -4945,8 +4945,9 @@ void Mle::Attacher::SendParentRequest(ParentRequestType aType)
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
if (aType == kToSelectedRouter)
{
TxMessage *messageToCurParent = static_cast<TxMessage *>(message->Clone());
TxMessage *messageToCurParent;
messageToCurParent = static_cast<TxMessage *>(Get<Mle>().mSocket.CloneMessage(*message));
VerifyOrExit(messageToCurParent != nullptr, error = kErrorNoBufs);
destination.SetToLinkLocalAddress(Get<Mle>().mParent.GetExtAddress());
+3 -3
View File
@@ -335,7 +335,7 @@ void InfraIf::SendUdp(const Ip6::Address &aSrcAddress,
if (aDestAddress.IsMulticast())
{
Message *loopbackMessage = aPayload.Clone();
Message *loopbackMessage = aPayload.Clone<kNoReservedHeader>();
VerifyOrQuit(loopbackMessage != nullptr);
Receive(GetNode(), *loopbackMessage);
@@ -396,7 +396,7 @@ void InfraIf::Receive(Node &aSrcNode, Message &aMessage)
if (headers.GetDestinationAddress().IsMulticast() || node.mInfraIf.HasAddress(headers.GetDestinationAddress()))
{
Mdns::AddressInfo senderAddress;
Message *payload = aMessage.Clone();
Message *payload = aMessage.Clone<kNoReservedHeader>();
VerifyOrQuit(payload != nullptr);
payload->RemoveHeader(sizeof(Ip6::Header) + sizeof(Ip6::Udp::Header));
@@ -432,7 +432,7 @@ void InfraIf::Receive(Node &aSrcNode, Message &aMessage)
VerifyOrExit(updatedHeader.GetHopLimit() > 1);
updatedHeader.SetHopLimit(updatedHeader.GetHopLimit() - 1);
messagePtr.Reset(aMessage.Clone());
messagePtr.Reset(aMessage.Clone<kNoReservedHeader>());
VerifyOrQuit(messagePtr != nullptr);
messagePtr->Write(0, updatedHeader);
+1 -1
View File
@@ -140,7 +140,7 @@ void Mdns::Receive(Instance &aInstance, Message &aMessage, bool aIsUnicast, cons
VerifyOrExit(aSenderAddress.mPort == kUdpPort);
}
message = aMessage.Clone();
message = aMessage.Clone<kNoReservedHeader>();
VerifyOrQuit(message != nullptr);
otPlatMdnsHandleReceive(&aInstance, message, aIsUnicast, &aSenderAddress);
+2 -2
View File
@@ -361,14 +361,14 @@ bool HandleResource(void *aContext, Uri aUri, Coap::Msg &aMsg)
SuccessOrQuit(Tlv::Find<MeshCoP::JoinerIidTlv>(aMsg.mMessage, joinerIid));
Log(" Received `RelayRx` from joiner - port:%u iid:%s", joinerPort, joinerIid.ToString().AsCString());
msgClone = aMsg.mMessage.Clone();
msgClone = aMsg.mMessage.Clone<kNoReservedHeader>();
VerifyOrQuit(msgClone != nullptr);
recvContext->mRelayRxMsgs.Enqueue(*msgClone);
break;
case kUriProxyRx:
Log(" Received `ProxyRx`");
msgClone = aMsg.mMessage.Clone();
msgClone = aMsg.mMessage.Clone<kNoReservedHeader>();
VerifyOrQuit(msgClone != nullptr);
recvContext->mProxyRxMsgs.Enqueue(*msgClone);
break;
+2 -2
View File
@@ -333,7 +333,7 @@ void TestDtlsSingleSession(void)
{
OwnedPtr<Message> msg(PrepareMessage(node0));
SuccessOrQuit(dtls0.Send(*msg->Clone()));
SuccessOrQuit(dtls0.Send(*msg->Clone<kNoReservedHeader>()));
nexus.AdvanceTime(100);
VerifyOrQuit(sDtlsLastReceive[node1.GetId()].GetLength() == msg->GetLength());
@@ -347,7 +347,7 @@ void TestDtlsSingleSession(void)
{
OwnedPtr<Message> msg(PrepareMessage(node1));
SuccessOrQuit(dtls1.Send(*msg->Clone()));
SuccessOrQuit(dtls1.Send(*msg->Clone<kNoReservedHeader>()));
nexus.AdvanceTime(100);
VerifyOrQuit(sDtlsLastReceive[node0.GetId()].GetLength() == msg->GetLength());
+1 -1
View File
@@ -218,7 +218,7 @@ void TestInPlaceAesCcmProcessing(void)
SuccessOrQuit(message->Append<uint8_t>(i & 0xff));
}
messageClone = message->Clone();
messageClone = message->Clone<kNoReservedHeader>();
VerifyOrQuit(messageClone != nullptr);
VerifyOrQuit(messageClone->GetLength() == msgLength);
+4 -4
View File
@@ -379,7 +379,7 @@ public:
message->SetOrigin(Message::kOriginHostUntrusted);
// Test 1: Default cloning
clone = message->Clone();
clone = message->Clone<kSameReservedHeader>();
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == message->GetLength());
@@ -397,7 +397,7 @@ public:
clone->Free();
// Test 2: Cloning with shorter length
clone = message->Clone(kLength / 2);
clone = message->Clone<kSameReservedHeader>(kLength / 2);
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == kLength / 2);
@@ -411,12 +411,12 @@ public:
// Test 3: Cloning with shorter length, offset change
message->SetOffset(80);
clone = message->Clone(kLength / 2);
clone = message->Clone<kNoReservedHeader>(kLength / 2);
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == kLength / 2);
VerifyOrQuit(clone->GetPriority() == message->GetPriority());
VerifyOrQuit(clone->GetReserved() == message->GetReserved());
VerifyOrQuit(clone->GetReserved() == 0);
VerifyOrQuit(clone->GetOffset() == 50); // Offset should be updated
VerifyOrQuit(clone->CompareBytes(0, buffer, kLength / 2));