diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index 3aeb1c838..8a34f2990 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -525,6 +525,48 @@ otError Message::Read(uint16_t aOffset, void *aBuf, uint16_t aLength) const return (ReadBytes(aOffset, aBuf, aLength) == aLength) ? OT_ERROR_NONE : OT_ERROR_PARSE; } +bool Message::CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength) const +{ + uint16_t bytesToCompare = aLength; + const uint8_t *bufPtr = reinterpret_cast(aBuf); + Chunk chunk; + + GetFirstChunk(aOffset, aLength, chunk); + + while (chunk.GetLength() > 0) + { + VerifyOrExit(memcmp(bufPtr, chunk.GetData(), chunk.GetLength()) == 0); + 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) const +{ + uint16_t bytesToCompare = aLength; + Chunk chunk; + + GetFirstChunk(aOffset, aLength, chunk); + + while (chunk.GetLength() > 0) + { + VerifyOrExit(aOtherMessage.CompareBytes(aOtherOffset, chunk.GetData(), chunk.GetLength())); + 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(aBuf); diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index ebb8919d0..dff485cad 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -710,6 +710,60 @@ public: return Read(aOffset, &aObject, sizeof(ObjectType)); } + /** + * This method compares the bytes in the message at a given offset with a given byte array. + * + * If there are fewer bytes available in the message than the requested @p aLength, the comparison is treated as + * failure (returns FALSE). + * + * @param[in] aOffset Byte offset within the message to read from for the comparison. + * @param[in] aBuf A pointer to a data buffer to compare with the bytes from message. + * @param[in] aLength Number of bytes in @p aBuf. + * + * @returns TRUE if there are enough bytes available in @p aMessage and they match the bytes from @p aBuf, + * FALSE otherwise. + * + */ + bool CompareBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength) const; + + /** + * This method compares the bytes in the message at a given offset with bytes read from another message. + * + * If either message has fewer bytes available than the requested @p aLength, the comparison is treated as failure + * (returns FALSE). + * + * @param[in] aOffset Byte offset within the message to read from for the comparison. + * @param[in] aOtherMessage The other message to compare with. + * @param[in] aOtherOffset Byte offset within @p aOtherMessage to read from for the comparison. + * @param[in] aLength Number of bytes to compare. + * + * @returns TRUE if there are enough bytes available in both messages and they all match. FALSE otherwise. + * + */ + bool CompareBytes(uint16_t aOffset, const Message &aOtherMessage, uint16_t aOtherOffset, uint16_t aLength) const; + + /** + * This method compares the bytes in the message at a given offset with an object. + * + * The bytes in the message are compared with the bytes in @p aObject. If there are fewer bytes available in the + * message than the requested object size, it is treated as failed comparison (returns FALSE). + * + * @tparam ObjectType The object type to compare with the bytes in message. + * + * @param[in] aOffset Byte offset within the message to read from for the comparison. + * @param[in] aObject A reference to the object to compare with the message bytes. + * + * @returns TRUE if there are enough bytes available in @p aMessage and they match the bytes in @p aObject, + * FALSE otherwise. + * + */ + template bool Compare(uint16_t aOffset, const ObjectType &aObject) const + { + static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); + + return CompareBytes(aOffset, &aObject, sizeof(ObjectType)); + } + /** * This method writes bytes to the message. * diff --git a/tests/unit/test_message.cpp b/tests/unit/test_message.cpp index 9a2e50ed8..dace85124 100644 --- a/tests/unit/test_message.cpp +++ b/tests/unit/test_message.cpp @@ -67,6 +67,8 @@ void TestMessage(void) message->WriteBytes(0, writeBuffer, kMaxSize); SuccessOrQuit(message->Read(0, readBuffer, kMaxSize), "Message::Read failed"); VerifyOrQuit(memcmp(writeBuffer, readBuffer, kMaxSize) == 0, "Message compare failed"); + VerifyOrQuit(message->CompareBytes(0, readBuffer, kMaxSize), "Message::CompareBytes failed"); + VerifyOrQuit(message->Compare(0, readBuffer), "Message::Compare failed"); VerifyOrQuit(message->GetLength() == kMaxSize, "Message::GetLength failed"); for (uint16_t offset = 0; offset < kMaxSize; offset++) @@ -82,11 +84,30 @@ void TestMessage(void) SuccessOrQuit(message->Read(0, readBuffer, kMaxSize), "Message::Read failed"); VerifyOrQuit(memcmp(writeBuffer, readBuffer, kMaxSize) == 0, "Message compare failed"); + VerifyOrQuit(message->Compare(0, writeBuffer), "Message::Compare() failed"); memset(readBuffer, 0, sizeof(readBuffer)); SuccessOrQuit(message->Read(offset, readBuffer, length), "Message::Read failed"); VerifyOrQuit(memcmp(readBuffer, &writeBuffer[offset], length) == 0, "Message compare failed"); VerifyOrQuit(memcmp(&readBuffer[length], zeroBuffer, kMaxSize - length) == 0, "Message read after length"); + + VerifyOrQuit(message->CompareBytes(offset, &writeBuffer[offset], length), "Message::CompareBytes() failed"); + + if (length == 0) + { + continue; + } + + // Change the first byte, and then last byte, and verify that + // `CompareBytes()` correctly fails. + + writeBuffer[offset]++; + VerifyOrQuit(!message->CompareBytes(offset, &writeBuffer[offset], length), "CompareBytes() failed"); + writeBuffer[offset]--; + + writeBuffer[offset + length - 1]++; + VerifyOrQuit(!message->CompareBytes(offset, &writeBuffer[offset], length), "CompareBytes() failed"); + writeBuffer[offset + length - 1]--; } // Verify `ReadBytes()` behavior when requested read length goes beyond available bytes in the message. @@ -97,10 +118,14 @@ void TestMessage(void) memset(readBuffer, 0, sizeof(readBuffer)); readLength = message->ReadBytes(offset, readBuffer, length); - VerifyOrQuit(readLength <= length, "Message::ReadBytes() returned longer length"); + + VerifyOrQuit(readLength < length, "Message::ReadBytes() returned longer length"); VerifyOrQuit(readLength == kMaxSize - offset, "Message::Read failed"); VerifyOrQuit(memcmp(readBuffer, &writeBuffer[offset], readLength) == 0, "Message compare failed"); VerifyOrQuit(memcmp(&readBuffer[readLength], zeroBuffer, kMaxSize - readLength) == 0, "read after length"); + + VerifyOrQuit(!message->CompareBytes(offset, readBuffer, length), "Message::CompareBytes failed"); + VerifyOrQuit(message->CompareBytes(offset, readBuffer, readLength), "Message::CompareBytes failed"); } } @@ -140,6 +165,11 @@ void TestMessage(void) VerifyOrQuit( memcmp(&readBuffer[dstOffset + bytesCopied], zeroBuffer, kMaxSize - bytesCopied - dstOffset) == 0, "read after length"); + + VerifyOrQuit(message->CompareBytes(srcOffset, *message2, dstOffset, bytesCopied), + "Message::CompareBytes with two messages failed"); + VerifyOrQuit(message2->CompareBytes(dstOffset, *message, srcOffset, bytesCopied), + "Message::CompareBytes with two messages failed"); } } }