[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.
diff --git a/src/core/api/message_api.cpp b/src/core/api/message_api.cpp
index 6ad8cbf..76c8bab 100644
--- a/src/core/api/message_api.cpp
+++ b/src/core/api/message_api.cpp
@@ -122,7 +122,7 @@
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(); }
diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp
index 1564ee5..737a510 100644
--- a/src/core/coap/coap.cpp
+++ b/src/core/coap/coap.cpp
@@ -1018,7 +1018,7 @@
FreeLastBlockResponse();
- mLastResponse = AsCoapMessagePtr(aResponse->Clone());
+ mLastResponse = AsCoapMessagePtr(aResponse->Clone<kNoReservedHeader>());
VerifyOrExit(mLastResponse != nullptr, error = kErrorNoBufs);
exit:
@@ -1549,7 +1549,7 @@
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 @@
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 @@
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 @@
MaintainCacheSize();
- responseClone = AsCoapMessagePtr(aTxMsg.mMessage.Clone());
+ responseClone = AsCoapMessagePtr(aTxMsg.mMessage.Clone<kNoReservedHeader>());
VerifyOrExit(responseClone != nullptr);
metadata.mExpireTime = TimerMilli::GetNow() + aExchangeLifetime;
diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp
index 6565f28..03deb47 100644
--- a/src/core/common/message.cpp
+++ b/src/core/common/message.cpp
@@ -785,10 +785,6 @@
}
}
-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 @@
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;
diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp
index f2bc6b2..3a729a6 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -157,6 +157,15 @@
};
/**
+ * 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.
*/
class Buffer : public otMessageBuffer, public LinkedListEntry<Buffer>
@@ -1100,34 +1109,11 @@
}
/**
- * 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.
@@ -1137,6 +1123,30 @@
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 @@
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;
+
/**
* @}
*/
diff --git a/src/core/common/message_allocator.hpp b/src/core/common/message_allocator.hpp
index 7f47c0d..148d14d 100644
--- a/src/core/common/message_allocator.hpp
+++ b/src/core/common/message_allocator.hpp
@@ -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 @@
*/
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;
diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp
index 66d2c41..9260ea0 100644
--- a/src/core/net/dns_client.cpp
+++ b/src/core/net/dns_client.cpp
@@ -1584,8 +1584,8 @@
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 @@
RecordServerAsLimitedToSingleQuestion(info.mConfig.GetServerSockAddr().GetAddress());
- secondQuery = aQuery.Clone();
+ secondQuery = mSocket.CloneMessage(aQuery);
VerifyOrExit(secondQuery != nullptr);
info.mQueryType = kServiceQueryTxt;
diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp
index 755653f..dae1b3f 100644
--- a/src/core/net/ip6.cpp
+++ b/src/core/net/ip6.cpp
@@ -184,7 +184,7 @@
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 @@
break;
case kCopyMessageToUse:
- aTargetPtr.Reset(aMessagePtr->Clone());
+ aTargetPtr.Reset(aMessagePtr->Clone<kNoReservedHeader>());
break;
}
diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp
index 7ec150b..0790210 100644
--- a/src/core/net/ip6_mpl.cpp
+++ b/src/core/net/ip6_mpl.cpp
@@ -342,7 +342,7 @@
#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 @@
nextTime.UpdateIfEarlier(metadata.mTransmissionTime);
- messageCopy = message.Clone();
+ messageCopy = message.Clone<kSameReservedHeader>();
}
else
{
diff --git a/src/core/net/sntp_client.cpp b/src/core/net/sntp_client.cpp
index 91230e4..5af0a33 100644
--- a/src/core/net/sntp_client.cpp
+++ b/src/core/net/sntp_client.cpp
@@ -128,7 +128,7 @@
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 @@
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:
diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp
index 218d176..0152854 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -4945,8 +4945,9 @@
#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());
diff --git a/tests/nexus/platform/nexus_infra_if.cpp b/tests/nexus/platform/nexus_infra_if.cpp
index 47f9cd4..cabd8a2 100644
--- a/tests/nexus/platform/nexus_infra_if.cpp
+++ b/tests/nexus/platform/nexus_infra_if.cpp
@@ -335,7 +335,7 @@
if (aDestAddress.IsMulticast())
{
- Message *loopbackMessage = aPayload.Clone();
+ Message *loopbackMessage = aPayload.Clone<kNoReservedHeader>();
VerifyOrQuit(loopbackMessage != nullptr);
Receive(GetNode(), *loopbackMessage);
@@ -396,7 +396,7 @@
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 @@
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);
diff --git a/tests/nexus/platform/nexus_mdns.cpp b/tests/nexus/platform/nexus_mdns.cpp
index 0592caf..377c251 100644
--- a/tests/nexus/platform/nexus_mdns.cpp
+++ b/tests/nexus/platform/nexus_mdns.cpp
@@ -140,7 +140,7 @@
VerifyOrExit(aSenderAddress.mPort == kUdpPort);
}
- message = aMessage.Clone();
+ message = aMessage.Clone<kNoReservedHeader>();
VerifyOrQuit(message != nullptr);
otPlatMdnsHandleReceive(&aInstance, message, aIsUnicast, &aSenderAddress);
diff --git a/tests/nexus/test_border_admitter.cpp b/tests/nexus/test_border_admitter.cpp
index 293092b..41fcae2 100644
--- a/tests/nexus/test_border_admitter.cpp
+++ b/tests/nexus/test_border_admitter.cpp
@@ -361,14 +361,14 @@
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;
diff --git a/tests/nexus/test_dtls.cpp b/tests/nexus/test_dtls.cpp
index 9e2ac70..2660d2b 100644
--- a/tests/nexus/test_dtls.cpp
+++ b/tests/nexus/test_dtls.cpp
@@ -333,7 +333,7 @@
{
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 @@
{
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());
diff --git a/tests/unit/test_aes.cpp b/tests/unit/test_aes.cpp
index b6351dd..f82f28a 100644
--- a/tests/unit/test_aes.cpp
+++ b/tests/unit/test_aes.cpp
@@ -218,7 +218,7 @@
SuccessOrQuit(message->Append<uint8_t>(i & 0xff));
}
- messageClone = message->Clone();
+ messageClone = message->Clone<kNoReservedHeader>();
VerifyOrQuit(messageClone != nullptr);
VerifyOrQuit(messageClone->GetLength() == msgLength);
diff --git a/tests/unit/test_message.cpp b/tests/unit/test_message.cpp
index f514227..17a8c57 100644
--- a/tests/unit/test_message.cpp
+++ b/tests/unit/test_message.cpp
@@ -379,7 +379,7 @@
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 @@
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 @@
// 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));