From e3f157402c2bca4b143f0b8477be63d227559241 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 1 Aug 2022 10:54:10 -0700 Subject: [PATCH] [frame-builder] add `Insert()`, `Remove()` and other helper methods (#7964) This commit adds `Insert()` and `Remove()` methods in `FrameBuilder`. `Insert` can be used to insert new content in the frame at a given offset moving any previously written content forward. `Remove()` can be used to remove previously appended content from the frame moving any content after the removed bytes backward. This commit also adds `GetRemainingLength()` to get the remaining length (number of bytes that can be appended) in the frame buffer, and `SetMaxLength()`. Unit test `test_frame_builder` is also updated to validate all the newly added methods. --- src/core/common/frame_builder.cpp | 22 +++++++++ src/core/common/frame_builder.hpp | 72 +++++++++++++++++++++++++++++- tests/unit/test_frame_builder.cpp | 74 +++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 1 deletion(-) diff --git a/src/core/common/frame_builder.cpp b/src/core/common/frame_builder.cpp index 6d95d1afb..bb9fbf3cd 100644 --- a/src/core/common/frame_builder.cpp +++ b/src/core/common/frame_builder.cpp @@ -96,4 +96,26 @@ void FrameBuilder::WriteBytes(uint16_t aOffset, const void *aBuffer, uint16_t aL memcpy(mBuffer + aOffset, aBuffer, aLength); } +Error FrameBuilder::InsertBytes(uint16_t aOffset, const void *aBuffer, uint16_t aLength) +{ + Error error = kErrorNone; + + OT_ASSERT(aOffset <= mLength); + + VerifyOrExit(CanAppend(aLength), error = kErrorNoBufs); + + memmove(mBuffer + aOffset + aLength, mBuffer + aOffset, mLength - aOffset); + memcpy(mBuffer + aOffset, aBuffer, aLength); + mLength += aLength; + +exit: + return error; +} + +void FrameBuilder::RemoveBytes(uint16_t aOffset, uint16_t aLength) +{ + memmove(mBuffer + aOffset, mBuffer + aOffset + aLength, mLength - aOffset - aLength); + mLength -= aLength; +} + } // namespace ot diff --git a/src/core/common/frame_builder.hpp b/src/core/common/frame_builder.hpp index 81150452f..42385ac88 100644 --- a/src/core/common/frame_builder.hpp +++ b/src/core/common/frame_builder.hpp @@ -77,13 +77,32 @@ public: uint16_t GetLength(void) const { return mLength; } /** - * This method returns the maximum length of frame. + * This method returns the maximum length of the frame. * * @returns The maximum frame length (max number of bytes in the frame buffer). * */ uint16_t GetMaxLength(void) const { return mMaxLength; } + /** + * This method sets the maximum length of the frame. + * + * This method does not perform any checks on the new given length. The caller MUST ensure that the specified max + * length is valid for the frame buffer. + * + * @param[in] aLength The maximum frame length. + * + */ + void SetMaxLength(uint16_t aLength) { mMaxLength = aLength; } + + /** + * This method returns the remaining length (number of bytes that can be appended) in the frame. + * + * @returns The remaining length. + * + */ + uint16_t GetRemainingLength(void) const { return mMaxLength - mLength; } + /** * This method indicates whether or not there are enough bytes remaining in the `FrameBuilder` buffer to append a * given number of bytes. @@ -227,6 +246,57 @@ public: WriteBytes(aOffset, &aObject, sizeof(ObjectType)); } + /** + * This method inserts bytes in `FrameBuilder` at a given offset, moving previous content forward. + * + * The caller MUST ensure that @p aOffset is within the current frame length (from 0 up to and including + * `GetLength()`). Otherwise the behavior of this method is undefined. + * + * @param[in] aOffset The offset to insert bytes. + * @param[in] aBuffer A pointer to a data buffer to insert. + * @param[in] aLength Number of bytes in @p aBuffer. + * + * @retval kErrorNone Successfully inserted the bytes. + * @retval kErrorNoBufs Insufficient available buffers to insert the bytes. + * + */ + Error InsertBytes(uint16_t aOffset, const void *aBuffer, uint16_t aLength); + + /** + * This method inserts an object in `FrameBuilder` at a given offset, moving previous content forward. + * + * The caller MUST ensure that @p aOffset is within the current frame length (from 0 up to and including + * `GetLength()`). Otherwise the behavior of this method is undefined. + * + * @tparam ObjectType The object type to insert. + * + * @param[in] aOffset The offset to insert bytes. + * @param[in] aObject A reference to the object to insert. + * + * @retval kErrorNone Successfully inserted the bytes. + * @retval kErrorNoBufs Insufficient available buffers to insert the bytes. + * + */ + template Error Insert(uint16_t aOffset, const ObjectType &aObject) + { + static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); + + return InsertBytes(aOffset, &aObject, sizeof(ObjectType)); + } + + /** + * This method removes a given number of bytes in `FrameBuilder` at a given offset, moving existing content + * after removed bytes backward. + * + * This method does not perform any bound checks. The caller MUST ensure that the given length and offset fits + * within the previously appended content. Otherwise the behavior of this method is undefined. + * + * @param[in] aOffset The offset to remove bytes from. + * @param[in] aLength The number of bytes to remove. + * + */ + void RemoveBytes(uint16_t aOffset, uint16_t aLength); + private: uint8_t *mBuffer; uint16_t mLength; diff --git a/tests/unit/test_frame_builder.cpp b/tests/unit/test_frame_builder.cpp index cbc09c01b..e089f666e 100644 --- a/tests/unit/test_frame_builder.cpp +++ b/tests/unit/test_frame_builder.cpp @@ -39,6 +39,7 @@ void TestFrameBuilder(void) { const uint8_t kData1[] = {0x01, 0x02, 0x03, 0x04, 0x05}; const uint8_t kData2[] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa}; + const uint8_t kData3[] = {0xca, 0xfe, 0xbe, 0xef}; static constexpr uint16_t kMaxBufferSize = sizeof(kData1) * 2 + sizeof(kData2); @@ -72,12 +73,25 @@ void TestFrameBuilder(void) VerifyOrQuit(frameBuilder.CanAppend(sizeof(buffer))); VerifyOrQuit(!frameBuilder.CanAppend(sizeof(buffer) + 1)); + frameBuilder.SetMaxLength(sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(frameBuilder.GetLength() == 0); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(kData1)); + VerifyOrQuit(memcmp(buffer, zeroBuffer, sizeof(buffer)) == 0); + VerifyOrQuit(frameBuilder.CanAppend(sizeof(kData1))); + VerifyOrQuit(!frameBuilder.CanAppend(sizeof(kData1) + 1)); + SuccessOrQuit(frameBuilder.Append(kData1)); VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); VerifyOrQuit(frameBuilder.GetBytes() == buffer); VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); VerifyOrQuit(memcmp(buffer + sizeof(kData1), zeroBuffer, sizeof(buffer) - sizeof(kData1)) == 0); + frameBuilder.SetMaxLength(sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(buffer)); + VerifyOrQuit(frameBuilder.CanAppend(sizeof(buffer) - sizeof(kData1))); + VerifyOrQuit(!frameBuilder.CanAppend(sizeof(buffer) - sizeof(kData1) + 1)); + SuccessOrQuit(frameBuilder.AppendUint8(0x01)); SuccessOrQuit(frameBuilder.AppendBigEndianUint16(0x0203)); SuccessOrQuit(frameBuilder.AppendLittleEndianUint16(0x0504)); @@ -135,6 +149,66 @@ void TestFrameBuilder(void) VerifyOrQuit(memcmp(buffer + sizeof(kData1), kData2, sizeof(kData2)) == 0); VerifyOrQuit(memcmp(buffer + sizeof(kData1) + sizeof(kData2), kData1, sizeof(kData1)) == 0); + frameBuilder.Init(buffer, sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(frameBuilder.GetLength() == 0); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(buffer)); + + offset = 0; + SuccessOrQuit(frameBuilder.Insert(offset, kData1)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + + offset = 0; + SuccessOrQuit(frameBuilder.Insert(offset, kData2)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData1, sizeof(kData1)) == 0); + + offset = sizeof(kData2); + SuccessOrQuit(frameBuilder.InsertBytes(offset, kData3, sizeof(kData3))); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2) + sizeof(kData3)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData3, sizeof(kData3)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2) + sizeof(kData3), kData1, sizeof(kData1)) == 0); + + offset = frameBuilder.GetLength(); + SuccessOrQuit(frameBuilder.Insert(offset, 0x77)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2) + sizeof(kData3) + sizeof(uint8_t)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData3, sizeof(kData3)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2) + sizeof(kData3), kData1, sizeof(kData1)) == 0); + VerifyOrQuit(buffer[sizeof(kData2) + sizeof(kData3) + sizeof(kData1)] == 0x77); + + offset = frameBuilder.GetLength() - 1; + frameBuilder.RemoveBytes(offset, sizeof(uint8_t)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2) + sizeof(kData3)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData3, sizeof(kData3)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2) + sizeof(kData3), kData1, sizeof(kData1)) == 0); + + offset = sizeof(kData2); + frameBuilder.RemoveBytes(offset, sizeof(kData3)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData1, sizeof(kData1)) == 0); + + offset = 0; + frameBuilder.RemoveBytes(offset, sizeof(kData2)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + + offset = 0; + frameBuilder.RemoveBytes(offset, sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetLength() == 0); + message->Free(); testFreeInstance(instance); }