[message] enhance Clone() to support different reserved header size (#12440)

This commit enhances the `Message::Clone()` method to support a custom
configuration. This allows callers to specify a different length and
reserved header size for the cloned message via the new overload
`Clone(uint16_t aLength, uint16_t aReserveHeader)`.

The existing `Clone()` overloads have been updated to utilize this new
configuration mechanism. Additionally, `Coap::Message::Clone()`
methods are updated to align with these changes.

A new unit test `TestCloning()` is added to `test_message.cpp` to
verify the behavior of `Clone()` with various configurations.
This commit is contained in:
Abtin Keshavarzian
2026-02-21 20:44:34 -08:00
committed by GitHub
parent 7d10faea6f
commit ecc57f765c
5 changed files with 187 additions and 60 deletions
+6 -2
View File
@@ -690,9 +690,13 @@ exit:
return hasSame;
}
Message *Message::Clone(uint16_t aLength) const
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
{
Message *message = static_cast<Message *>(ot::Message::Clone(aLength));
Message *message = static_cast<Message *>(ot::Message::Clone(aLength, aReserveHeader));
VerifyOrExit(message != nullptr);
+19 -6
View File
@@ -826,11 +826,22 @@ public:
*/
Error AppendPayloadMarker(void);
/**
* 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 this CoAP 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`, `InterfaceId`, and `Priority` fields on the
* 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 payload bytes to copy.
@@ -840,15 +851,17 @@ public:
Message *Clone(uint16_t aLength) const;
/**
* Creates a copy of the message.
* Creates a copy of the message using a given configuration.
*
* It allocates the new message from the same message pool as the original one and copies the entire payload. The
* `Type`, `SubType`, `LinkSecurity`, `Offset`, `InterfaceId`, and `Priority` fields on the cloned message are also
* copied from the original one.
* 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.
*
* @param[in] aLength Number of message bytes to copy.
* @param[in] aReserveHeader Number of header bytes to reserve in the new cloned message.
*
* @returns A pointer to the message or `nullptr` if insufficient message buffers are available.
*/
Message *Clone(void) const { return Clone(GetLength()); }
Message *Clone(uint16_t aLength, uint16_t aReserveHeader) const;
/**
* Returns a pointer to the next message after this as a `Coap::Message`.
+28 -24
View File
@@ -785,39 +785,43 @@ void Message::WriteBytesFromMessage(uint16_t aWriteOffset,
}
}
Message *Message::Clone(uint16_t aLength) const
{
Error error = kErrorNone;
Message *messageCopy;
Settings settings(IsLinkSecurityEnabled() ? kWithLinkSecurity : kNoLinkSecurity, GetPriority());
uint16_t offset;
Message *Message::Clone(void) const { return Clone(GetLength()); }
aLength = Min(GetLength(), aLength);
messageCopy = Get<MessagePool>().Allocate(GetType(), GetReserved(), settings);
VerifyOrExit(messageCopy != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = messageCopy->AppendBytesFromMessage(*this, 0, aLength));
Message *Message::Clone(uint16_t aLength) const { return Clone(aLength, GetReserved()); }
Message *Message::Clone(uint16_t aLength, uint16_t aReserveHeader) const
{
Error error = kErrorNone;
Message *clone;
LinkSecurityMode linkSecurityMode = IsLinkSecurityEnabled() ? kWithLinkSecurity : kNoLinkSecurity;
clone = Get<MessagePool>().Allocate(GetType(), aReserveHeader, Settings(linkSecurityMode, GetPriority()));
VerifyOrExit(clone != nullptr, error = kErrorNoBufs);
aLength = Min(aLength, GetLength());
SuccessOrExit(error = clone->AppendBytesFromMessage(*this, 0, aLength));
// Copy selected message information.
offset = Min(GetOffset(), aLength);
messageCopy->SetOffset(offset);
clone->SetOffset(Min(GetOffset(), aLength));
messageCopy->SetSubType(GetSubType());
messageCopy->SetLoopbackToHostAllowed(IsLoopbackToHostAllowed());
messageCopy->SetOrigin(GetOrigin());
messageCopy->SetTimestamp(GetTimestamp());
messageCopy->SetMeshDest(GetMeshDest());
messageCopy->SetPanId(GetPanId());
messageCopy->SetChannel(GetChannel());
messageCopy->SetRssAverager(GetRssAverager());
messageCopy->SetLqiAverager(GetLqiAverager());
clone->SetSubType(GetSubType());
clone->SetLoopbackToHostAllowed(IsLoopbackToHostAllowed());
clone->SetOrigin(GetOrigin());
clone->SetTimestamp(GetTimestamp());
clone->SetMeshDest(GetMeshDest());
clone->SetPanId(GetPanId());
clone->SetChannel(GetChannel());
clone->SetRssAverager(GetRssAverager());
clone->SetLqiAverager(GetLqiAverager());
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
messageCopy->SetTimeSync(IsTimeSync());
clone->SetTimeSync(IsTimeSync());
#endif
exit:
FreeAndNullMessageOnError(messageCopy, error);
return messageCopy;
FreeAndNullMessageOnError(clone, error);
return clone;
}
Error Message::GetLinkInfo(ThreadLinkInfo &aLinkInfo) const
+24 -9
View File
@@ -70,6 +70,7 @@ struct otMessage
namespace ot {
class UnitTester;
template <typename UintType> class CrcCalculator;
namespace Crypto {
@@ -286,6 +287,7 @@ class Message : public otMessage, public Buffer, public GetProvider<Message>
friend class MessagePool;
friend class MessageQueue;
friend class PriorityQueue;
friend class ot::UnitTester;
public:
/**
@@ -1093,26 +1095,39 @@ public:
/**
* 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`, `InterfaceId`, and `Priority` fields on the
* cloned message are also copied from the original one.
* 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.
*
* @param[in] aLength Number of payload bytes to copy.
* @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.
* Creates a copy of the message using a given configuration.
*
* It allocates the new message from the same message pool as the original one and copies the entire payload. The
* `Type`, `SubType`, `LinkSecurity`, `Offset`, `InterfaceId`, and `Priority` fields on the cloned message are also
* copied from the original one.
* 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.
*
* @param[in] aLength Number of message bytes to copy.
* @param[in] aReserveHeader Number of header bytes to reserve in the new cloned message.
*
* @returns A pointer to the message or `nullptr` if insufficient message buffers are available.
*/
Message *Clone(void) const { return Clone(GetLength()); }
Message *Clone(uint16_t aLength, uint16_t aReserveHeader) const;
/**
* Returns the datagram tag used for 6LoWPAN fragmentation or the identification used for IPv6
+110 -19
View File
@@ -47,7 +47,6 @@ void TestMessage(uint16_t aReservedLength)
MessagePool *messagePool;
Message *message;
Message *message2;
Message *messageCopy;
uint8_t writeBuffer[kMaxSize];
uint8_t readBuffer[kMaxSize];
uint8_t zeroBuffer[kMaxSize];
@@ -78,26 +77,8 @@ void TestMessage(uint16_t aReservedLength)
VerifyOrQuit(message->Compare(0, readBuffer));
VerifyOrQuit(message->GetLength() == kMaxSize);
// Verify `Clone()` behavior
message->SetOffset(15);
messageCopy = message->Clone();
VerifyOrQuit(messageCopy->GetOffset() == message->GetOffset());
SuccessOrQuit(messageCopy->Read(0, readBuffer, kMaxSize));
VerifyOrQuit(memcmp(writeBuffer, readBuffer, kMaxSize) == 0);
VerifyOrQuit(messageCopy->CompareBytes(0, readBuffer, kMaxSize));
VerifyOrQuit(messageCopy->Compare(0, readBuffer));
VerifyOrQuit(messageCopy->GetLength() == kMaxSize);
VerifyOrQuit(messageCopy->GetType() == message->GetType());
VerifyOrQuit(messageCopy->GetSubType() == message->GetSubType());
VerifyOrQuit(messageCopy->IsLinkSecurityEnabled() == message->IsLinkSecurityEnabled());
VerifyOrQuit(messageCopy->GetPriority() == message->GetPriority());
VerifyOrQuit(messageCopy->IsLoopbackToHostAllowed() == message->IsLoopbackToHostAllowed());
VerifyOrQuit(messageCopy->GetOrigin() == message->GetOrigin());
VerifyOrQuit(messageCopy->Compare(0, readBuffer));
message->SetOffset(0);
messageCopy->Free();
for (uint16_t offset = 0; offset < kMaxSize; offset++)
{
for (uint16_t length = 0; length <= kMaxSize - offset; length++)
@@ -359,6 +340,114 @@ void TestMessage(uint16_t aReservedLength)
testFreeInstance(instance);
}
class UnitTester
{
public:
static void TestCloning(void)
{
static constexpr uint16_t kLength = 100;
Instance *instance;
MessagePool *messagePool;
Message *message;
Message *clone;
uint8_t buffer[kLength];
printf("TestCloning()\n");
instance = static_cast<Instance *>(testInitInstance());
VerifyOrQuit(instance != nullptr);
messagePool = &instance->Get<MessagePool>();
// Create a message with specific properties
message = messagePool->Allocate(Message::kTypeIp6, /* aReserveHeader */ 60);
VerifyOrQuit(message != nullptr);
Random::NonCrypto::FillBuffer(buffer, sizeof(buffer));
SuccessOrQuit(message->Append(buffer));
VerifyOrQuit(message->GetLength() == kLength);
VerifyOrQuit(message->GetType() == Message::kTypeIp6);
VerifyOrQuit(message->GetReserved() == 60);
SuccessOrQuit(message->SetPriority(Message::kPriorityHigh));
message->SetOffset(10);
message->SetSubType(Message::kSubTypeMle);
message->SetLinkSecurityEnabled(true);
message->SetLoopbackToHostAllowed(true);
message->SetOrigin(Message::kOriginHostUntrusted);
// Test 1: Default cloning
clone = message->Clone();
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == message->GetLength());
VerifyOrQuit(clone->GetPriority() == message->GetPriority());
VerifyOrQuit(clone->GetReserved() == message->GetReserved());
VerifyOrQuit(clone->GetOffset() == message->GetOffset());
VerifyOrQuit(clone->GetType() == message->GetType());
VerifyOrQuit(clone->GetSubType() == message->GetSubType());
VerifyOrQuit(clone->IsLinkSecurityEnabled() == message->IsLinkSecurityEnabled());
VerifyOrQuit(clone->IsLoopbackToHostAllowed() == message->IsLoopbackToHostAllowed());
VerifyOrQuit(clone->GetOrigin() == message->GetOrigin());
VerifyOrQuit(clone->Compare(0, buffer));
VerifyOrQuit(clone->CompareBytes(0, buffer, kLength));
clone->Free();
// Test 2: Cloning with shorter length
clone = message->Clone(kLength / 2);
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == kLength / 2);
VerifyOrQuit(clone->GetPriority() == message->GetPriority());
VerifyOrQuit(clone->GetReserved() == message->GetReserved());
VerifyOrQuit(clone->GetOffset() == 10); // Offset should be preserved if < new length
VerifyOrQuit(clone->CompareBytes(0, buffer, kLength / 2));
clone->Free();
// Test 3: Cloning with shorter length, offset change
message->SetOffset(80);
clone = message->Clone(kLength / 2);
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == kLength / 2);
VerifyOrQuit(clone->GetPriority() == message->GetPriority());
VerifyOrQuit(clone->GetReserved() == message->GetReserved());
VerifyOrQuit(clone->GetOffset() == 50); // Offset should be updated
VerifyOrQuit(clone->CompareBytes(0, buffer, kLength / 2));
clone->Free();
// Test 4: Cloning with overridden reserved header size
clone = message->Clone(message->GetLength(), 0);
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == message->GetLength());
VerifyOrQuit(clone->GetPriority() == message->GetPriority());
VerifyOrQuit(clone->GetReserved() == 0);
VerifyOrQuit(clone->Compare(0, buffer));
clone->Free();
// Test 5: Cloning with all shorter length and different reserved header size
clone = message->Clone(kLength / 4, 40);
VerifyOrQuit(clone != nullptr);
VerifyOrQuit(clone->GetLength() == kLength / 4);
VerifyOrQuit(clone->GetReserved() == 40);
VerifyOrQuit(clone->CompareBytes(0, buffer, kLength / 4));
clone->Free();
message->Free();
testFreeInstance(instance);
}
};
void TestAppender(void)
{
const uint8_t kData1[] = {0x01, 0x02, 0x03, 0x04};
@@ -459,7 +548,9 @@ int main(void)
ot::TestMessage(reservedLength);
}
ot::UnitTester::TestCloning();
ot::TestAppender();
printf("All tests passed\n");
return 0;
}