[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:
Abtin Keshavarzian
2021-11-10 09:28:01 -08:00
committed by GitHub
parent 7184999022
commit 2fb1756d61
7 changed files with 440 additions and 0 deletions
+1
View File
@@ -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 \
+2
View File
@@ -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",
+1
View File
@@ -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
+2
View File
@@ -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 \
+111
View File
@@ -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
+176
View File
@@ -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_
+147
View File
@@ -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;
}