mirror of
https://github.com/espressif/openthread.git
synced 2026-07-28 14:47:46 +00:00
[common] adding HeapData class (#7129)
This commit adds new class `HeapData` which represents a heap allocated data buffer of a given variable length. This class provides methods to set/copy the `HeapData` from/to a buffer or a `Message. This commit also adds unit test for the new class.
This commit is contained in:
@@ -221,6 +221,7 @@ LOCAL_SRC_FILES := \
|
||||
src/core/coap/coap_secure.cpp \
|
||||
src/core/common/crc16.cpp \
|
||||
src/core/common/error.cpp \
|
||||
src/core/common/heap_data.cpp \
|
||||
src/core/common/heap_string.cpp \
|
||||
src/core/common/instance.cpp \
|
||||
src/core/common/logging.cpp \
|
||||
|
||||
@@ -387,6 +387,8 @@ openthread_core_files = [
|
||||
"common/error.cpp",
|
||||
"common/error.hpp",
|
||||
"common/extension.hpp",
|
||||
"common/heap_data.cpp",
|
||||
"common/heap_data.hpp",
|
||||
"common/heap_string.cpp",
|
||||
"common/heap_string.hpp",
|
||||
"common/instance.cpp",
|
||||
|
||||
@@ -94,6 +94,7 @@ set(COMMON_SOURCES
|
||||
coap/coap_secure.cpp
|
||||
common/crc16.cpp
|
||||
common/error.cpp
|
||||
common/heap_data.cpp
|
||||
common/heap_string.cpp
|
||||
common/instance.cpp
|
||||
common/logging.cpp
|
||||
|
||||
@@ -184,6 +184,7 @@ SOURCES_COMMON = \
|
||||
coap/coap_secure.cpp \
|
||||
common/crc16.cpp \
|
||||
common/error.cpp \
|
||||
common/heap_data.cpp \
|
||||
common/heap_string.cpp \
|
||||
common/instance.cpp \
|
||||
common/logging.cpp \
|
||||
@@ -426,6 +427,7 @@ HEADERS_COMMON = \
|
||||
common/equatable.hpp \
|
||||
common/error.hpp \
|
||||
common/extension.hpp \
|
||||
common/heap_data.hpp \
|
||||
common/heap_string.hpp \
|
||||
common/instance.hpp \
|
||||
common/iterator_utils.hpp \
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 `HeapData` (a heap allocated data).
|
||||
*/
|
||||
|
||||
#include "heap_data.hpp"
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
Error HeapData::SetFrom(const uint8_t *aBuffer, uint16_t aLength)
|
||||
{
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = UpdateBuffer(aLength));
|
||||
VerifyOrExit(aLength != 0);
|
||||
|
||||
SuccessOrAssert(mData.CopyBytesFrom(aBuffer, aLength));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error HeapData::SetFrom(const Message &aMessage)
|
||||
{
|
||||
Error error;
|
||||
uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
|
||||
|
||||
SuccessOrExit(error = UpdateBuffer(length));
|
||||
VerifyOrExit(length != 0);
|
||||
|
||||
SuccessOrAssert(aMessage.Read(aMessage.GetOffset(), mData.GetBytes(), mData.GetLength()));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void HeapData::SetFrom(HeapData &&aHeapData)
|
||||
{
|
||||
Free();
|
||||
TakeFrom(aHeapData);
|
||||
}
|
||||
|
||||
void HeapData::Free(void)
|
||||
{
|
||||
Instance::HeapFree(mData.GetBytes());
|
||||
mData.Init(nullptr, 0);
|
||||
}
|
||||
|
||||
Error HeapData::UpdateBuffer(uint16_t aNewLength)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(aNewLength != mData.GetLength());
|
||||
|
||||
Instance::HeapFree(mData.GetBytes());
|
||||
|
||||
if (aNewLength == 0)
|
||||
{
|
||||
mData.Init(nullptr, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t *newBuffer = static_cast<uint8_t *>(Instance::HeapCAlloc(aNewLength, sizeof(uint8_t)));
|
||||
|
||||
VerifyOrExit(newBuffer != nullptr, error = kErrorNoBufs);
|
||||
mData.Init(newBuffer, aNewLength);
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void HeapData::TakeFrom(HeapData &aHeapData)
|
||||
{
|
||||
mData.Init(aHeapData.mData.GetBytes(), aHeapData.GetLength());
|
||||
aHeapData.mData.Init(nullptr, 0);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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 includes definitions for `HeapData` (heap allocated data).
|
||||
*/
|
||||
|
||||
#ifndef HEAP_DATA_HPP_
|
||||
#define HEAP_DATA_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include "common/data.hpp"
|
||||
#include "common/message.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
/**
|
||||
* This class represents a heap allocated data.
|
||||
*
|
||||
*/
|
||||
class HeapData
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This constructor initializes the `HeapData` as empty.
|
||||
*
|
||||
*/
|
||||
HeapData(void) { mData.Init(nullptr, 0); }
|
||||
|
||||
/**
|
||||
* This is the move constructor for `HeapData`.
|
||||
*
|
||||
* `HeapData` is non-copyable (copy constructor is deleted) but move constructor is provided to allow it to to be
|
||||
* used as return type (return by value) from functions/methods (which will then use move semantics).
|
||||
*
|
||||
* @param[in] aHeapData An rvalue reference to another `HeapData` to move from.
|
||||
*
|
||||
*/
|
||||
HeapData(HeapData &&aHeapData) { TakeFrom(aHeapData); }
|
||||
|
||||
/**
|
||||
* This is the destructor for `HeapData` object.
|
||||
*
|
||||
*/
|
||||
~HeapData(void) { Free(); }
|
||||
|
||||
/**
|
||||
* This method indicates whether or not the `HeapData` is null (i.e., it was never successfully set or it was
|
||||
* freed).
|
||||
*
|
||||
* @retval TRUE The `HeapData` is null.
|
||||
* @retval FALSE The `HeapData` is not null.
|
||||
*
|
||||
*/
|
||||
bool IsNull(void) const { return (mData.GetBytes() == nullptr); }
|
||||
|
||||
/**
|
||||
* This method returns a pointer to the `HeapData` bytes buffer.
|
||||
*
|
||||
* @returns A pointer to data buffer or `nullptr` if the `HeapData` is null (never set or freed).
|
||||
*
|
||||
*/
|
||||
const uint8_t *GetBytes(void) const { return mData.GetBytes(); }
|
||||
|
||||
/**
|
||||
* This method returns the `HeapData` length.
|
||||
*
|
||||
* @returns The data length (number of bytes) or zero if the `HeadpData` is null.
|
||||
*
|
||||
*/
|
||||
uint16_t GetLength(void) const { return mData.GetLength(); }
|
||||
|
||||
/**
|
||||
* This method sets the `HeapData` from the content of a given buffer.
|
||||
*
|
||||
* @param[in] aBuffer The buffer to copy bytes from.
|
||||
* @param[in] aLength The buffer length (number of bytes).
|
||||
*
|
||||
* @retval kErrorNone Successfully set the `HeapData`.
|
||||
* @retval kErrorNoBufs Failed to allocate buffer.
|
||||
*
|
||||
*/
|
||||
Error SetFrom(const uint8_t *aBuffer, uint16_t aLength);
|
||||
|
||||
/**
|
||||
* This method sets the `HeapData` from the content of a given message.
|
||||
*
|
||||
* The bytes are copied from current offset in @p aMessage till the end of the message.
|
||||
*
|
||||
* @param[in] aMessage The message to copy bytes from (starting from offset till the end of message).
|
||||
* @param[in] aLength The buffer length (number of bytes).
|
||||
*
|
||||
* @retval kErrorNone Successfully set the `HeapData`.
|
||||
* @retval kErrorNoBufs Failed to allocate buffer.
|
||||
*
|
||||
*/
|
||||
Error SetFrom(const Message &aMessage);
|
||||
|
||||
/**
|
||||
* This method sets the `HeapData` from another one (move semantics).
|
||||
*
|
||||
* @param[in] aHeapData The other `HeapData` to set from (rvalue reference).
|
||||
*
|
||||
*/
|
||||
void SetFrom(HeapData &&aHeapData);
|
||||
|
||||
/**
|
||||
* This method appends the bytes from `HeapData` to a given message.
|
||||
*
|
||||
* @param[in] aMessage The message to append the bytes into.
|
||||
*
|
||||
* @retval kErrorNone Successfully copied the bytes from `HeapData` into @p aMessage.
|
||||
* @retval kErrorNoBufs Failed to allocate buffer.
|
||||
*
|
||||
*/
|
||||
Error CopyBytesTo(Message &aMessage) const { return aMessage.AppendBytes(mData.GetBytes(), mData.GetLength()); }
|
||||
|
||||
/**
|
||||
* This method copies the bytes from `HeapData` into a given buffer.
|
||||
*
|
||||
* It is up to the caller to ensure that @p aBuffer has enough space for the current data length.
|
||||
*
|
||||
* @param[in] aBuffer A pointer to buffer to copy the bytes into.
|
||||
*
|
||||
*/
|
||||
void CopyBytesTo(uint8_t *aBuffer) const { return mData.CopyBytesTo(aBuffer); }
|
||||
|
||||
/**
|
||||
* This method frees any buffer allocated by the `HeapData`.
|
||||
*
|
||||
* The `HeapData` destructor will automatically call `Free()`. This method allows caller to free the buffer
|
||||
* explicitly.
|
||||
*
|
||||
*/
|
||||
void Free(void);
|
||||
|
||||
HeapData(const HeapData &) = delete;
|
||||
HeapData &operator=(const HeapData &) = delete;
|
||||
|
||||
private:
|
||||
Error UpdateBuffer(uint16_t aNewLength);
|
||||
void TakeFrom(HeapData &aHeapData);
|
||||
|
||||
MutableData<kWithUint16Length> mData;
|
||||
};
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // HEAP_DATA_HPP_
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "test_util.hpp"
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/heap_data.hpp"
|
||||
#include "common/heap_string.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -87,6 +88,9 @@ void TestHeapString(void)
|
||||
HeapString str2;
|
||||
const char *oldBuffer;
|
||||
|
||||
printf("====================================================================================\n");
|
||||
printf("TestHeapString\n\n");
|
||||
|
||||
printf("------------------------------------------------------------------------------------\n");
|
||||
printf("After constructor\n\n");
|
||||
VerifyString("str1", str1, nullptr);
|
||||
@@ -146,11 +150,154 @@ void TestHeapString(void)
|
||||
printf("\n -- PASS\n");
|
||||
}
|
||||
|
||||
void PrintData(const HeapData &aData)
|
||||
{
|
||||
DumpBuffer("data", aData.GetBytes(), aData.GetLength());
|
||||
}
|
||||
|
||||
template <uint16_t kLength> void VerifyData(const HeapData &aData, const uint8_t (&aArray)[kLength])
|
||||
{
|
||||
VerifyData(aData, &aArray[0], kLength);
|
||||
}
|
||||
|
||||
static const uint8_t kTestValue = 0x77;
|
||||
|
||||
// Function returning a `HeapData` by value.
|
||||
HeapData GetData(void)
|
||||
{
|
||||
HeapData data;
|
||||
|
||||
SuccessOrQuit(data.SetFrom(&kTestValue, sizeof(kTestValue)));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void VerifyData(const HeapData &aData, const uint8_t *aBytes, uint16_t aLength)
|
||||
{
|
||||
static constexpr uint16_t kMaxLength = 100;
|
||||
uint8_t buffer[kMaxLength];
|
||||
|
||||
PrintData(aData);
|
||||
|
||||
if (aLength == 0)
|
||||
{
|
||||
VerifyOrQuit(aData.IsNull());
|
||||
VerifyOrQuit(aData.GetBytes() == nullptr);
|
||||
VerifyOrQuit(aData.GetLength() == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
VerifyOrQuit(!aData.IsNull());
|
||||
VerifyOrQuit(aData.GetBytes() != nullptr);
|
||||
VerifyOrQuit(aData.GetLength() == aLength);
|
||||
VerifyOrQuit(memcmp(aData.GetBytes(), aBytes, aLength) == 0, "Data content is incorrect");
|
||||
|
||||
aData.CopyBytesTo(buffer);
|
||||
VerifyOrQuit(memcmp(buffer, aBytes, aLength) == 0, "CopyBytesTo() failed");
|
||||
}
|
||||
}
|
||||
|
||||
void TestHeapData(void)
|
||||
{
|
||||
Instance * instance;
|
||||
MessagePool * messagePool;
|
||||
Message * message;
|
||||
HeapData data;
|
||||
const uint8_t *oldBuffer;
|
||||
|
||||
static const uint8_t kData1[] = {10, 20, 3, 15, 100, 0, 60, 16};
|
||||
static const uint8_t kData2[] = "OpenThread HeapData";
|
||||
static const uint8_t kData3[] = {0xaa, 0xbb, 0xcc};
|
||||
static const uint8_t kData4[] = {0x11, 0x22, 0x33};
|
||||
|
||||
instance = static_cast<Instance *>(testInitInstance());
|
||||
VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
|
||||
|
||||
messagePool = &instance->Get<MessagePool>();
|
||||
VerifyOrQuit((message = messagePool->New(Message::kTypeIp6, 0)) != nullptr, "Message::New failed");
|
||||
|
||||
message->SetOffset(0);
|
||||
|
||||
printf("\n\n====================================================================================\n");
|
||||
printf("TestHeapData\n\n");
|
||||
|
||||
printf("------------------------------------------------------------------------------------\n");
|
||||
printf("After constructor\n");
|
||||
VerifyData(data, nullptr, 0);
|
||||
|
||||
printf("------------------------------------------------------------------------------------\n");
|
||||
printf("SetFrom(aBuffer, aLength)\n");
|
||||
|
||||
SuccessOrQuit(data.SetFrom(kData1, sizeof(kData1)));
|
||||
VerifyData(data, kData1);
|
||||
|
||||
SuccessOrQuit(data.SetFrom(kData2, sizeof(kData2)));
|
||||
VerifyData(data, kData2);
|
||||
|
||||
SuccessOrQuit(data.SetFrom(kData3, sizeof(kData3)));
|
||||
VerifyData(data, kData3);
|
||||
oldBuffer = data.GetBytes();
|
||||
|
||||
SuccessOrQuit(data.SetFrom(kData4, sizeof(kData4)));
|
||||
VerifyData(data, kData4);
|
||||
VerifyOrQuit(oldBuffer == data.GetBytes(), "did not reuse old buffer on same data length");
|
||||
|
||||
SuccessOrQuit(data.SetFrom(kData4, 0));
|
||||
VerifyData(data, nullptr, 0);
|
||||
|
||||
printf("------------------------------------------------------------------------------------\n");
|
||||
printf("SetFrom(aMessage)\n");
|
||||
|
||||
SuccessOrQuit(message->Append(kData2));
|
||||
SuccessOrQuit(data.SetFrom(*message));
|
||||
VerifyData(data, kData2);
|
||||
|
||||
SuccessOrQuit(message->Append(kData3));
|
||||
SuccessOrQuit(data.SetFrom(*message));
|
||||
PrintData(data);
|
||||
VerifyOrQuit(data.GetLength() == message->GetLength());
|
||||
|
||||
message->SetOffset(sizeof(kData2));
|
||||
SuccessOrQuit(data.SetFrom(*message));
|
||||
VerifyData(data, kData3);
|
||||
|
||||
printf("------------------------------------------------------------------------------------\n");
|
||||
printf("Free()\n");
|
||||
|
||||
data.Free();
|
||||
VerifyData(data, nullptr, 0);
|
||||
|
||||
data.Free();
|
||||
VerifyData(data, nullptr, 0);
|
||||
|
||||
printf("------------------------------------------------------------------------------------\n");
|
||||
printf("CopyBytesTo(aMessage)\n");
|
||||
|
||||
SuccessOrQuit(message->SetLength(0));
|
||||
|
||||
SuccessOrQuit(data.CopyBytesTo(*message));
|
||||
VerifyOrQuit(message->GetLength() == 0, "CopyBytesTo() failed");
|
||||
|
||||
SuccessOrQuit(data.SetFrom(kData1, sizeof(kData1)));
|
||||
VerifyData(data, kData1);
|
||||
SuccessOrQuit(data.CopyBytesTo(*message));
|
||||
VerifyOrQuit(message->GetLength() == data.GetLength(), "CopyBytesTo() failed");
|
||||
VerifyOrQuit(message->Compare(0, kData1), "CopyBytesTo() failed");
|
||||
|
||||
printf("------------------------------------------------------------------------------------\n");
|
||||
printf("SetFrom() move semantics\n\n");
|
||||
data.SetFrom(GetData());
|
||||
VerifyData(data, &kTestValue, sizeof(kTestValue));
|
||||
|
||||
printf("\n -- PASS\n");
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::TestHeapString();
|
||||
ot::TestHeapData();
|
||||
printf("\nAll tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user