mirror of
https://github.com/espressif/openthread.git
synced 2026-08-04 01:47:47 +00:00
[common] adding Appender class (#7176)
This commit adds a `Appender` class which acts as a wrapper over either a `Message` or a data buffer (e.g., `MutableData`) and provides different flavors of `Append()` method. This class helps in construction of message content where the destination can be either a `Message` or a buffer. This commit also adds a unit test for the new class.
This commit is contained in:
committed by
Jonathan Hui
parent
0e8b6604cb
commit
7c284b1517
@@ -219,6 +219,7 @@ LOCAL_SRC_FILES := \
|
||||
src/core/coap/coap.cpp \
|
||||
src/core/coap/coap_message.cpp \
|
||||
src/core/coap/coap_secure.cpp \
|
||||
src/core/common/appender.cpp \
|
||||
src/core/common/crc16.cpp \
|
||||
src/core/common/error.cpp \
|
||||
src/core/common/heap.cpp \
|
||||
|
||||
@@ -371,6 +371,8 @@ openthread_core_files = [
|
||||
"coap/coap_message.hpp",
|
||||
"coap/coap_secure.cpp",
|
||||
"coap/coap_secure.hpp",
|
||||
"common/appender.cpp",
|
||||
"common/appender.hpp",
|
||||
"common/arg_macros.hpp",
|
||||
"common/array.hpp",
|
||||
"common/as_core_type.hpp",
|
||||
|
||||
@@ -92,6 +92,7 @@ set(COMMON_SOURCES
|
||||
coap/coap.cpp
|
||||
coap/coap_message.cpp
|
||||
coap/coap_secure.cpp
|
||||
common/appender.cpp
|
||||
common/crc16.cpp
|
||||
common/error.cpp
|
||||
common/heap.cpp
|
||||
|
||||
@@ -182,6 +182,7 @@ SOURCES_COMMON = \
|
||||
coap/coap.cpp \
|
||||
coap/coap_message.cpp \
|
||||
coap/coap_secure.cpp \
|
||||
common/appender.cpp \
|
||||
common/crc16.cpp \
|
||||
common/error.cpp \
|
||||
common/heap.cpp \
|
||||
@@ -414,6 +415,7 @@ HEADERS_COMMON = \
|
||||
coap/coap.hpp \
|
||||
coap/coap_message.hpp \
|
||||
coap/coap_secure.hpp \
|
||||
common/appender.hpp \
|
||||
common/arg_macros.hpp \
|
||||
common/array.hpp \
|
||||
common/as_core_type.hpp \
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2021, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file implements the `Appender` class.
|
||||
*/
|
||||
|
||||
#include "appender.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
Appender::Appender(Message &aMessage)
|
||||
: mType(kMessage)
|
||||
{
|
||||
mShared.mMessage.mMessage = &aMessage;
|
||||
mShared.mMessage.mStartOffset = aMessage.GetLength();
|
||||
}
|
||||
|
||||
Appender::Appender(uint8_t *aBuffer, uint16_t aSize)
|
||||
: mType(kBuffer)
|
||||
{
|
||||
mShared.mBuffer.mStart = aBuffer;
|
||||
mShared.mBuffer.mCur = aBuffer;
|
||||
mShared.mBuffer.mEnd = aBuffer + aSize;
|
||||
}
|
||||
|
||||
Error Appender::AppendBytes(const void *aBuffer, uint16_t aLength)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
switch (mType)
|
||||
{
|
||||
case kMessage:
|
||||
error = mShared.mMessage.mMessage->AppendBytes(aBuffer, aLength);
|
||||
break;
|
||||
|
||||
case kBuffer:
|
||||
VerifyOrExit(aLength <= static_cast<uint16_t>(mShared.mBuffer.mEnd - mShared.mBuffer.mCur),
|
||||
error = kErrorNoBufs);
|
||||
memcpy(mShared.mBuffer.mCur, aBuffer, aLength);
|
||||
mShared.mBuffer.mCur += aLength;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
uint16_t Appender::GetAppendedLength(void) const
|
||||
{
|
||||
uint16_t length = 0;
|
||||
|
||||
switch (mType)
|
||||
{
|
||||
case kMessage:
|
||||
length = mShared.mMessage.mMessage->GetLength() - mShared.mMessage.mStartOffset;
|
||||
break;
|
||||
|
||||
case kBuffer:
|
||||
length = static_cast<uint16_t>(mShared.mBuffer.mCur - mShared.mBuffer.mStart);
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void Appender::GetAsData(Data<kWithUint16Length> &aData)
|
||||
{
|
||||
aData.Init(mShared.mBuffer.mStart, GetAppendedLength());
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2021, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file defines OpenThread `Appender` class.
|
||||
*/
|
||||
|
||||
#ifndef APPENDER_HPP_
|
||||
#define APPENDER_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include "common/data.hpp"
|
||||
#include "common/message.hpp"
|
||||
#include "common/type_traits.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
/**
|
||||
* The `Appender` class acts as a wrapper over either a `Message` or a data buffer and provides different flavors of
|
||||
* `Append()` method.
|
||||
*
|
||||
* This class helps in construction of message content where the destination can be either a `Message` or a buffer.
|
||||
*
|
||||
*/
|
||||
class Appender
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This enumeration represent the `Appender` Type (whether appending to a `Message` or data buffer).
|
||||
*
|
||||
*/
|
||||
enum Type : uint8_t
|
||||
{
|
||||
kMessage, ///< `Appender` appends to a `Message`
|
||||
kBuffer, ///< `Appender` appends to a buffer.
|
||||
};
|
||||
|
||||
/**
|
||||
* This constructor initializes the `Appender` to append to a `Message`.
|
||||
*
|
||||
* New content is appended to the end of @p aMessage, growing its length.
|
||||
*
|
||||
* @param[in] aMessage The message to append to.
|
||||
*
|
||||
*/
|
||||
explicit Appender(Message &aMessage);
|
||||
|
||||
/**
|
||||
* This constructor initializes the `Appender` to append in a given a buffer
|
||||
*
|
||||
* New content is append in the buffer starting from @p aBuffer up to is size @p aSize. `Appender` does not allow
|
||||
* content to be appended beyond the size of the buffer.
|
||||
*
|
||||
* @param[in] aBuffer A pointer to start of buffer.
|
||||
* @param[in] aSize The maximum size of @p aBuffer (number of available bytes in buffer).
|
||||
*
|
||||
*/
|
||||
Appender(uint8_t *aBuffer, uint16_t aSize);
|
||||
|
||||
/**
|
||||
* This method indicates the `Appender` type (whether appending to a `Message` or data buffer).
|
||||
*
|
||||
* @returns The type of `Appender`.
|
||||
*
|
||||
*/
|
||||
Type GetType(void) const { return mType; }
|
||||
|
||||
/**
|
||||
* This method appends bytes to the `Appender` object
|
||||
*
|
||||
* @param[in] aBuffer A pointer to a data buffer (MUST NOT be `nullptr`) to append.
|
||||
* @param[in] aLength The number of bytes to append.
|
||||
*
|
||||
* @retval kErrorNone Successfully appended the bytes.
|
||||
* @retval kErrorNoBufs Insufficient available buffers.
|
||||
*
|
||||
*/
|
||||
Error AppendBytes(const void *aBuffer, uint16_t aLength);
|
||||
|
||||
/**
|
||||
* This method appends an object to the end of the `Appender` object.
|
||||
*
|
||||
* @tparam ObjectType The object type to append to the message.
|
||||
*
|
||||
* @param[in] aObject A reference to the object to append to the message.
|
||||
*
|
||||
* @retval kErrorNone Successfully appended the object.
|
||||
* @retval kErrorNoBufs Insufficient available buffers to append @p aObject.
|
||||
*
|
||||
*/
|
||||
template <typename ObjectType> Error Append(const ObjectType &aObject)
|
||||
{
|
||||
static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
|
||||
|
||||
return AppendBytes(&aObject, sizeof(ObjectType));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the number of bytes appended so far using `Appender` methods.
|
||||
*
|
||||
* This method can be used independent of the `Type` of `Appender`.
|
||||
*
|
||||
* @returns The number of byes appended so far.
|
||||
*
|
||||
*/
|
||||
uint16_t GetAppendedLength(void) const;
|
||||
|
||||
/**
|
||||
* This method returns the `Message` associated with `Appender`.
|
||||
*
|
||||
* This method MUST be used when `GetType() == kMessage`. Otherwise its behavior is undefined.
|
||||
*
|
||||
* @returns The `Message` instance associated with `Appender`.
|
||||
*
|
||||
*/
|
||||
Message &GetMessage(void) { return *mShared.mMessage.mMessage; }
|
||||
|
||||
/**
|
||||
* This method returns a pointer to the start of the data buffer associated with `Appender`.
|
||||
*
|
||||
* This method MUST be used when `GetType() == kBuffer`. Otherwise its behavior is undefined.
|
||||
*
|
||||
* @returns A pointer to the start of the data buffer associated with `Appender`.
|
||||
*
|
||||
*/
|
||||
uint8_t *GetBufferStart(void) { return mShared.mBuffer.mStart; }
|
||||
|
||||
/**
|
||||
* This method gets the data buffer associated with `Appender` as a `Data`.
|
||||
*
|
||||
* This method MUST be used when `GetType() == kBuffer`. Otherwise its behavior is undefined.
|
||||
*
|
||||
* @pram[out] aData A reference to a `Data` to output the data buffer.
|
||||
*
|
||||
*/
|
||||
void GetAsData(Data<kWithUint16Length> &aData);
|
||||
|
||||
private:
|
||||
Type mType;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
Message *mMessage;
|
||||
uint16_t mStartOffset;
|
||||
} mMessage;
|
||||
|
||||
struct
|
||||
{
|
||||
uint8_t *mStart;
|
||||
uint8_t *mCur;
|
||||
uint8_t *mEnd;
|
||||
} mBuffer;
|
||||
} mShared;
|
||||
};
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // APPENDER_HPP_
|
||||
@@ -26,6 +26,7 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "common/appender.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "common/message.hpp"
|
||||
@@ -53,6 +54,8 @@ void TestMessage(void)
|
||||
uint8_t readBuffer[kMaxSize];
|
||||
uint8_t zeroBuffer[kMaxSize];
|
||||
|
||||
printf("TestMessage\n");
|
||||
|
||||
memset(zeroBuffer, 0, sizeof(zeroBuffer));
|
||||
|
||||
instance = static_cast<Instance *>(testInitInstance());
|
||||
@@ -236,11 +239,101 @@ void TestMessage(void)
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestAppender(void)
|
||||
{
|
||||
const uint8_t kData1[] = {0x01, 0x02, 0x03, 0x04};
|
||||
const uint8_t kData2[] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa};
|
||||
|
||||
static constexpr uint16_t kMaxBufferSize = sizeof(kData1) * 2 + sizeof(kData2);
|
||||
|
||||
Instance * instance;
|
||||
Message * message;
|
||||
uint8_t buffer[kMaxBufferSize];
|
||||
uint8_t zeroBuffer[kMaxBufferSize];
|
||||
Appender bufAppender(buffer, sizeof(buffer));
|
||||
Data<kWithUint16Length> data;
|
||||
|
||||
printf("TestAppender\n");
|
||||
|
||||
instance = static_cast<Instance *>(testInitInstance());
|
||||
VerifyOrQuit(instance != nullptr);
|
||||
|
||||
message = instance->Get<MessagePool>().New(Message::kTypeIp6, 0);
|
||||
VerifyOrQuit(message != nullptr);
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
memset(zeroBuffer, 0, sizeof(zeroBuffer));
|
||||
|
||||
// Test Buffer Appender
|
||||
VerifyOrQuit(bufAppender.GetType() == Appender::kBuffer);
|
||||
VerifyOrQuit(bufAppender.GetBufferStart() == buffer);
|
||||
VerifyOrQuit(bufAppender.GetAppendedLength() == 0);
|
||||
|
||||
SuccessOrQuit(bufAppender.AppendBytes(kData1, sizeof(kData1)));
|
||||
DumpBuffer("Data1", buffer, sizeof(buffer));
|
||||
VerifyOrQuit(bufAppender.GetAppendedLength() == sizeof(kData1));
|
||||
VerifyOrQuit(bufAppender.GetBufferStart() == buffer);
|
||||
VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0);
|
||||
VerifyOrQuit(memcmp(buffer + sizeof(kData1), zeroBuffer, sizeof(buffer) - sizeof(kData1)) == 0);
|
||||
|
||||
SuccessOrQuit(bufAppender.AppendBytes(kData2, sizeof(kData2)));
|
||||
DumpBuffer("Data1+Data2", buffer, sizeof(buffer));
|
||||
VerifyOrQuit(bufAppender.GetAppendedLength() == sizeof(kData1) + sizeof(kData2));
|
||||
VerifyOrQuit(bufAppender.GetBufferStart() == buffer);
|
||||
VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0);
|
||||
VerifyOrQuit(memcmp(buffer + sizeof(kData1), kData2, sizeof(kData2)) == 0);
|
||||
VerifyOrQuit(memcmp(buffer + sizeof(kData1) + sizeof(kData2), zeroBuffer,
|
||||
sizeof(buffer) - sizeof(kData1) - sizeof(kData2)) == 0);
|
||||
|
||||
VerifyOrQuit(bufAppender.Append(kData2) == kErrorNoBufs);
|
||||
|
||||
SuccessOrQuit(bufAppender.AppendBytes(kData1, sizeof(kData1)));
|
||||
DumpBuffer("Data1+Data2+Data1", buffer, sizeof(buffer));
|
||||
VerifyOrQuit(bufAppender.GetAppendedLength() == sizeof(kData1) + sizeof(kData2) + sizeof(kData1));
|
||||
VerifyOrQuit(bufAppender.GetBufferStart() == buffer);
|
||||
VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0);
|
||||
VerifyOrQuit(memcmp(buffer + sizeof(kData1), kData2, sizeof(kData2)) == 0);
|
||||
VerifyOrQuit(memcmp(buffer + sizeof(kData1) + sizeof(kData2), kData1, sizeof(kData1)) == 0);
|
||||
|
||||
VerifyOrQuit(bufAppender.Append<uint8_t>(0) == kErrorNoBufs);
|
||||
|
||||
bufAppender.GetAsData(data);
|
||||
VerifyOrQuit(data.GetBytes() == buffer);
|
||||
VerifyOrQuit(data.GetLength() == sizeof(buffer));
|
||||
|
||||
// Test Message Appender
|
||||
|
||||
SuccessOrQuit(message->Append(kData2));
|
||||
VerifyOrQuit(message->Compare(0, kData2));
|
||||
|
||||
{
|
||||
Appender msgAppender(*message);
|
||||
uint16_t offset = message->GetLength();
|
||||
|
||||
VerifyOrQuit(msgAppender.GetType() == Appender::kMessage);
|
||||
|
||||
SuccessOrQuit(msgAppender.AppendBytes(kData1, sizeof(kData1)));
|
||||
VerifyOrQuit(msgAppender.GetAppendedLength() == sizeof(kData1));
|
||||
|
||||
VerifyOrQuit(message->GetLength() == sizeof(kData2) + sizeof(kData1));
|
||||
VerifyOrQuit(message->Compare(offset, kData1));
|
||||
|
||||
SuccessOrQuit(msgAppender.AppendBytes(kData2, sizeof(kData2)));
|
||||
VerifyOrQuit(msgAppender.GetAppendedLength() == sizeof(kData1) + sizeof(kData2));
|
||||
VerifyOrQuit(message->Compare(offset, kData1));
|
||||
VerifyOrQuit(message->Compare(offset + sizeof(kData1), kData2));
|
||||
}
|
||||
|
||||
message->Free();
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::TestMessage();
|
||||
ot::TestAppender();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user