diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp index 7ff6f94f8..fad6fca2b 100644 --- a/src/core/coap/coap_message.cpp +++ b/src/core/coap/coap_message.cpp @@ -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(ot::Message::Clone(aLength)); + Message *message = static_cast(ot::Message::Clone(aLength, aReserveHeader)); VerifyOrExit(message != nullptr); diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp index d70974b7e..caefae8e7 100644 --- a/src/core/coap/coap_message.hpp +++ b/src/core/coap/coap_message.hpp @@ -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`. diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index ffa595b31..947a722a1 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -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().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().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 diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 289a60359..39f0863db 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -70,6 +70,7 @@ struct otMessage namespace ot { +class UnitTester; template class CrcCalculator; namespace Crypto { @@ -286,6 +287,7 @@ class Message : public otMessage, public Buffer, public GetProvider 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 diff --git a/tests/unit/test_message.cpp b/tests/unit/test_message.cpp index a31d45778..f5142279b 100644 --- a/tests/unit/test_message.cpp +++ b/tests/unit/test_message.cpp @@ -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(testInitInstance()); + VerifyOrQuit(instance != nullptr); + + messagePool = &instance->Get(); + + // 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; }