[common] adding Heap::Array<Type> a heap allocated flexible-length array (#7420)

This commit adds `Heap::Array<Type>` class which allocates the
buffer to store array elements from the heap. The `Array`
implementation automatically grows the buffer when new entries
are added. It also provides optional method `ReserveCapacity()`
to allow user to allocate and reserve a certain capacity
(number of elements user expects to add).

The `Array` can safely be used with element `Type`s that are
themselves heap allocated (manage allocated items). The `Array`
implementation uses the move constructor and destructor of the
`Type` class to ensure that copying the entries when growing the
array is performed efficiently and that the removed entries are
properly deleted.

The `Array` implementation also provides helper methods to search
in the array, e.g., `Find()`, `FindMatching()`, `Contains()`, and
`ContainsMatching()`. It also supports range-based `for` loop
iteration.

This commit also adds a detailed unit test `test_heap_array` which
covers behavior of `Array` with a simple `uint16_t` entry type and
a more complex entry type (validating the constructors and destructor
of entry are properly invoked by `Array` implementation).
This commit is contained in:
Abtin Keshavarzian
2022-03-08 17:55:02 -08:00
committed by Jonathan Hui
parent 465518ec64
commit 4b31f57014
6 changed files with 1073 additions and 0 deletions
+1
View File
@@ -394,6 +394,7 @@ openthread_core_files = [
"common/heap.cpp",
"common/heap.hpp",
"common/heap_allocatable.hpp",
"common/heap_array.hpp",
"common/heap_data.cpp",
"common/heap_data.hpp",
"common/heap_string.cpp",
+1
View File
@@ -435,6 +435,7 @@ HEADERS_COMMON = \
common/extension.hpp \
common/heap.hpp \
common/heap_allocatable.hpp \
common/heap_array.hpp \
common/heap_data.hpp \
common/heap_string.hpp \
common/instance.hpp \
+551
View File
@@ -0,0 +1,551 @@
/*
* Copyright (c) 2022, 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 `Heap::Array` (a heap allocated array of flexible length).
*/
#ifndef HEAP_ARRAY_HPP_
#define HEAP_ARRAY_HPP_
#include "openthread-core-config.h"
#include <stdint.h>
#include <stdio.h>
#include "common/array.hpp"
#include "common/code_utils.hpp"
#include "common/error.hpp"
#include "common/heap.hpp"
#include "common/new.hpp"
namespace ot {
namespace Heap {
/**
* This class represents a heap allocated array.
*
* The buffer to store the elements is allocated from heap and is managed by the `Heap::Array` class itself. The `Array`
* implementation will automatically grow the buffer when new entries are added. The `Heap::Array` destructor will
* always free the allocated buffer.
*
* The `Type` class MUST provide a move constructor `Type(Type &&aOther)` (or a copy constructor if no move constructor
* is provided). This constructor is used to move existing elements when array buffer is grown (new buffer is
* allocated) to make room for new elements.
*
* @tparam Type The array element type.
* @tparam kCapacityIncrements Number of elements to allocate at a time when updating the array buffer.
*
*/
template <typename Type, uint16_t kCapacityIncrements = 2> class Array
{
public:
using IndexType = uint16_t;
/**
* This constructor initializes the `Array` as empty.
*
*/
Array(void)
: mArray(nullptr)
, mLength(0)
, mCapacity(0)
{
}
/**
* This is the destructor for `Array` object.
*
*/
~Array(void) { Free(); }
/**
* This method frees any buffer allocated by the `Array`.
*
* The `Array` destructor will automatically call `Free()`. This method allows caller to free buffer explicitly.
*
*/
void Free(void)
{
Clear();
Heap::Free(mArray);
mArray = nullptr;
mCapacity = 0;
}
/**
* This method clears the array.
*
* Note that `Clear()` method (unlike `Free()`) does not free the allocated buffer and therefore does not change
* the current capacity of the array.
*
* This method invokes `Type` destructor on all cleared existing elements of array.
*
*/
void Clear(void)
{
for (Type &entry : *this)
{
entry.~Type();
}
mLength = 0;
}
/**
* This method returns the current array length (number of elements in the array).
*
* @returns The array length.
*
*/
IndexType GetLength(void) const { return mLength; }
/**
* This method returns a raw pointer to the array buffer.
*
* The returned raw pointer is valid only while the `Array` remains unchanged.
*
* @returns A pointer to the array buffer or `nullptr` if the array is empty.
*
*/
const Type *AsCArray(void) const { return (mLength != 0) ? mArray : nullptr; }
/**
* This method returns the current capacity of array (number of elements that can fit in current allocated buffer).
*
* The allocated buffer and array capacity are automatically increased (by the `Array` itself) when new elements
* are added to array. Removing elements does not change the buffer and the capacity. A desired capacity can be
* reserved using `ReserveCapacity()` method.
*
* @returns The current capacity of the array.
*
*/
IndexType GetCapacity(void) const { return mCapacity; }
/**
* This method allocates buffer to reserve a given capacity for array.
*
* If the requested @p aCapacity is smaller than the current length of the array, capacity remains unchanged.
*
* @param[in] aCapacity The target capacity for the array.
*
* @retval kErrorNone Array was successfully updated to support @p aCapacity.
* @retval kErrorNoBufs Could not allocate buffer.
*
*/
Error ReserveCapacity(IndexType aCapacity) { return Allocate(aCapacity); }
/**
* This method sets the array by taking the buffer from another given array (using move semantics).
*
* @param[in] aOther The other `Heap::Array` to take from (rvalue reference).
*
*/
void TakeFrom(Array &&aOther)
{
Free();
mArray = aOther.mArray;
mLength = aOther.mLength;
mCapacity = aOther.mCapacity;
aOther.mArray = nullptr;
aOther.mLength = 0;
aOther.mCapacity = 0;
}
/**
* This method overloads the `[]` operator to get the element at a given index.
*
* This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
*
* @param[in] aIndex The index to get.
*
* @returns A reference to the element in array at @p aIndex.
*
*/
Type &operator[](IndexType aIndex) { return mArray[aIndex]; }
/**
* This method overloads the `[]` operator to get the element at a given index.
*
* This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
*
* @param[in] aIndex The index to get.
*
* @returns A reference to the element in array at @p aIndex.
*
*/
const Type &operator[](IndexType aIndex) const { return mArray[aIndex]; }
/**
* This method gets a pointer to the element at a given index.
*
* Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
* pointer is valid only while the `Array` remains unchanged.
*
* @param[in] aIndex The index to get.
*
* @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
*
*/
Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
/**
* This method gets a pointer to the element at a given index.
*
* Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
* pointer is valid only while the `Array` remains unchanged.
*
* @param[in] aIndex The index to get.
*
* @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
*
*/
const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
/**
* This method gets a pointer to the element at the front of the array (first element).
*
* The returned pointer is valid only while the `Array` remains unchanged.
*
* @returns A pointer to the front element or `nullptr` if array is empty.
*
*/
Type *Front(void) { return At(0); }
/**
* This method gets a pointer to the element at the front of the array (first element).
*
* The returned pointer is valid only while the `Array` remains unchanged.
*
* @returns A pointer to the front element or `nullptr` if array is empty.
*
*/
const Type *Front(void) const { return At(0); }
/**
* This method gets a pointer to the element at the back of the array (last element).
*
* The returned pointer is valid only while the `Array` remains unchanged.
*
* @returns A pointer to the back element or `nullptr` if array is empty.
*
*/
Type *Back(void) { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
/**
* This method gets a pointer to the element at the back of the array (last element).
*
* The returned pointer is valid only while the `Array` remains unchanged.
*
* @returns A pointer to the back element or `nullptr` if array is empty.
*
*/
const Type *Back(void) const { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
/**
* This method appends a new entry to the end of the array.
*
* This method requires the `Type` to provide a copy constructor of format `Type(const Type &aOther)` to init the
* new element in the array from @p aEntry.
*
* @param[in] aEntry The new entry to push back.
*
* @retval kErrorNone Successfully pushed back @p aEntry to the end of the array.
* @retval kErrorNoBufs Could not allocate buffer to grow the array.
*
*/
Error PushBack(const Type &aEntry)
{
Error error = kErrorNone;
if (mLength == mCapacity)
{
SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
}
new (&mArray[mLength++]) Type(aEntry);
exit:
return error;
}
/**
* This method appends a new entry to the end of the array.
*
* This method requires the `Type` to provide a copy constructor of format `Type(Type &&aOther)` to init the
* new element in the array from @p aEntry.
*
* @param[in] aEntry The new entry to push back (an rvalue reference)
*
* @retval kErrorNone Successfully pushed back @p aEntry to the end of the array.
* @retval kErrorNoBufs Could not allocate buffer to grow the array.
*
*/
Error PushBack(Type &&aEntry)
{
Error error = kErrorNone;
if (mLength == mCapacity)
{
SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
}
new (&mArray[mLength++]) Type(static_cast<Type &&>(aEntry));
exit:
return error;
}
/**
* This method appends a new entry to the end of the array.
*
* On success, this method returns a pointer to the newly appended element in the array for the caller to
* initialize and use. This method uses the `Type(void)` default constructor on the newly appended element (if not
* `nullptr`).
*
* The returned pointer is valid only while the `Array` remains unchanged.
*
* @return A pointer to the newly appended element or `nullptr` if could not allocate buffer to grow the array
*
*/
Type *PushBack(void)
{
Type *newEntry = nullptr;
if (mLength == mCapacity)
{
SuccessOrExit(Allocate(mCapacity + kCapacityIncrements));
}
newEntry = new (&mArray[mLength++]) Type();
exit:
return newEntry;
}
/**
* This method removes the last element in the array.
*
* This method will invoke the `Type` destructor on the removed element.
*
* @returns A pointer to the removed element from the array, or `nullptr` if array is empty.
*
*/
void PopBack(void)
{
if (mLength > 0)
{
mArray[mLength - 1].~Type();
mLength--;
}
}
/**
* This method returns the index of an element in the array.
*
* The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
*
* @param[in] aElement A reference to an element in the array.
*
* @returns The index of @p aElement in the array.
*
*/
IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - mArray); }
/**
* This method finds the first match of a given entry in the array.
*
* This method uses `==` operator on `Type` to compare the array element with @p aEntry. The returned pointer is
* valid only while the `Array` remains unchanged.
*
* @param[in] aEntry The entry to search for within the array.
*
* @returns A pointer to matched array element, or `nullptr` if a match could not be found.
*
*/
Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); }
/**
* This method finds the first match of a given entry in the array.
*
* This method uses `==` operator to compare the array elements with @p aEntry. The returned pointer is valid only
* while the `Array` remains unchanged.
*
* @param[in] aEntry The entry to search for within the array.
*
* @returns A pointer to matched array element, or `nullptr` if a match could not be found.
*
*/
const Type *Find(const Type &aEntry) const
{
const Type *matched = nullptr;
for (const Type &element : *this)
{
if (element == aEntry)
{
matched = &element;
break;
}
}
return matched;
}
/**
* This method indicates whether or not a match to given entry exists in the array.
*
* This method uses `==` operator on `Type` to compare the array elements with @p aEntry.
*
* @param[in] aEntry The entry to search for within the array.
*
* @retval TRUE The array contains a matching element with @p aEntry.
* @retval FALSE The array does not contain a matching element with @p aEntry.
*
*/
bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
/**
* This template method finds the first element in the array matching a given indicator.
*
* The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
* in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
* `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
*
* bool Type::Matches(const Indicator &aIndicator) const
*
* The returned pointer is valid only while the `Array` remains unchanged.
*
* @param[in] aIndicator An indicator to match with elements in the array.
*
* @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
*
*/
template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
{
return AsNonConst(AsConst(this)->FindMatching(aIndicator));
}
/**
* This template method finds the first element in the array matching a given indicator.
*
* The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
* in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
* `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
*
* bool Type::Matches(const Indicator &aIndicator) const
*
* The returned pointer is valid only while the `Array` remains unchanged.
*
* @param[in] aIndicator An indicator to match with elements in the array.
*
* @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
*
*/
template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
{
const Type *matched = nullptr;
for (const Type &element : *this)
{
if (element.Matches(aIndicator))
{
matched = &element;
break;
}
}
return matched;
}
/**
* This template method indicates whether or not the array contains an element matching a given indicator.
*
* The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
* in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
* `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
*
* bool Type::Matches(const Indicator &aIndicator) const
*
* @param[in] aIndicator An indicator to match with elements in the array.
*
* @retval TRUE The array contains a matching element with @p aIndicator.
* @retval FALSE The array does not contain a matching element with @p aIndicator.
*
*/
template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
{
return FindMatching(aIndicator) != nullptr;
}
// The following methods are intended to support range-based `for`
// loop iteration over the array elements and should not be used
// directly.
Type * begin(void) { return (mLength > 0) ? mArray : nullptr; }
Type * end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; }
const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; }
const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; }
Array(const Array &) = delete;
Array &operator=(const Array &) = delete;
private:
Error Allocate(IndexType aCapacity)
{
Error error = kErrorNone;
Type *newArray;
VerifyOrExit((aCapacity != mCapacity) && (aCapacity >= mLength));
newArray = static_cast<Type *>(Heap::CAlloc(aCapacity, sizeof(Type)));
VerifyOrExit(newArray != nullptr, error = kErrorNoBufs);
for (IndexType index = 0; index < mLength; index++)
{
new (&newArray[index]) Type(static_cast<Type &&>(mArray[index]));
mArray[index].~Type();
}
Heap::Free(mArray);
mArray = newArray;
mCapacity = aCapacity;
exit:
return error;
}
Type * mArray;
IndexType mLength;
IndexType mCapacity;
};
} // namespace Heap
} // namespace ot
#endif // HEAP_ARRAY_HPP_
+21
View File
@@ -341,6 +341,27 @@ target_link_libraries(ot-test-heap
add_test(NAME ot-test-heap COMMAND ot-test-heap)
add_executable(ot-test-heap-array
test_heap_array.cpp
)
target_include_directories(ot-test-heap-array
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-heap-array
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-heap-array
PRIVATE
${COMMON_LIBS}
)
add_test(NAME ot-test-heap-array COMMAND ot-test-heap-array)
add_executable(ot-test-heap-string
test_heap_string.cpp
)
+5
View File
@@ -124,6 +124,7 @@ check_PROGRAMS += \
ot-test-ecdsa \
ot-test-flash \
ot-test-heap \
ot-test-heap-array \
ot-test-heap-string \
ot-test-hkdf-sha256 \
ot-test-hmac-sha256 \
@@ -243,6 +244,10 @@ ot_test_heap_LDADD = $(COMMON_LDADD)
ot_test_heap_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
ot_test_heap_SOURCES = $(COMMON_SOURCES) test_heap.cpp
ot_test_heap_array_LDADD = $(COMMON_LDADD)
ot_test_heap_array_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
ot_test_heap_array_SOURCES = $(COMMON_SOURCES) test_heap_array.cpp
ot_test_heap_string_LDADD = $(COMMON_LDADD)
ot_test_heap_string_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
ot_test_heap_string_SOURCES = $(COMMON_SOURCES) test_heap_string.cpp
+494
View File
@@ -0,0 +1,494 @@
/*
* Copyright (c) 2022, 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.
*/
#include "test_platform.h"
#include <string.h>
#include <openthread/config.h>
#include "test_util.hpp"
#include "common/heap_array.hpp"
#include "common/type_traits.hpp"
namespace ot {
// Counters tracking number of times `Entry` constructor and
// destructor are invoked. These are used to verify that the `Array`
// properly calls constructor/destructor when allocating and copying
// array buffer.
static uint16_t sConstructorCalls = 0;
static uint16_t sDestructorCalls = 0;
class Entry
{
public:
Entry(void)
: mValue(0)
, mInitialized(true)
{
sConstructorCalls++;
}
explicit Entry(uint16_t aValue)
: mValue(aValue)
, mInitialized(true)
{
sConstructorCalls++;
}
Entry(const Entry &aEntry)
: mValue(aEntry.mValue)
, mInitialized(true)
{
sConstructorCalls++;
}
~Entry(void) { sDestructorCalls++; }
uint16_t GetValue(void) const { return mValue; }
void SetValue(uint16_t aValue) { mValue = aValue; }
bool IsInitialized(void) const { return mInitialized; }
bool operator==(const Entry &aOther) const { return mValue == aOther.mValue; }
bool Matches(uint16_t aValue) const { return mValue == aValue; }
private:
uint16_t mValue;
bool mInitialized;
};
template <typename EntryType>
void VerifyEntry(const EntryType &aEntry, const Heap::Array<EntryType, 2> &aArray, int aExpectedValue)
{
// Verify the entry in a given array with an expected value.
// Specializations of this template are defined below for `EntryType`
// being `uint16_t` or `Entry` class.
OT_UNUSED_VARIABLE(aEntry);
OT_UNUSED_VARIABLE(aArray);
OT_UNUSED_VARIABLE(aExpectedValue);
VerifyOrQuit(false, "Specializations of this template method MUST be used instead");
}
template <> void VerifyEntry(const uint16_t &aEntry, const Heap::Array<uint16_t, 2> &aArray, int aExpectedValue)
{
OT_UNUSED_VARIABLE(aArray);
VerifyOrQuit(aEntry == static_cast<uint16_t>(aExpectedValue));
}
template <> void VerifyEntry(const Entry &aEntry, const Heap::Array<Entry, 2> &aArray, int aExpectedValue)
{
VerifyOrQuit(aEntry.IsInitialized());
VerifyOrQuit(aEntry.GetValue() == static_cast<uint16_t>(aExpectedValue));
VerifyOrQuit(aArray.ContainsMatching(aEntry.GetValue()));
VerifyOrQuit(aArray.FindMatching(aEntry.GetValue()) == &aEntry);
}
template <typename EntryType, typename... Args> void VerifyArray(const Heap::Array<EntryType, 2> &aArray, Args... aArgs)
{
// Verify that array content matches the `aArgs` sequence
// (which can be empty).
constexpr uint16_t kUnusedValue = 0xffff;
int values[] = {aArgs..., 0};
uint16_t index = 0;
printf(" - Array (len:%u, capacity:%u) = { ", aArray.GetLength(), aArray.GetCapacity());
VerifyOrQuit(aArray.GetLength() == sizeof...(aArgs));
if (aArray.GetLength() == 0)
{
VerifyOrQuit(aArray.AsCArray() == nullptr);
VerifyOrQuit(aArray.Front() == nullptr);
VerifyOrQuit(aArray.Back() == nullptr);
}
else
{
VerifyOrQuit(aArray.AsCArray() != nullptr);
}
for (const EntryType &entry : aArray)
{
VerifyOrQuit(index < aArray.GetLength());
VerifyEntry(entry, aArray, values[index]);
VerifyOrQuit(aArray.Contains(entry));
VerifyOrQuit(aArray.Find(entry) == &entry);
VerifyOrQuit(aArray.IndexOf(entry) == index);
if (index == 0)
{
VerifyOrQuit(aArray.Front() == &entry);
}
if (index == aArray.GetLength())
{
VerifyOrQuit(aArray.Back() == &entry);
}
printf("%u ", values[index]);
index++;
}
VerifyOrQuit(index == aArray.GetLength());
VerifyOrQuit(!aArray.Contains(EntryType(kUnusedValue)));
VerifyOrQuit(aArray.Find(EntryType(kUnusedValue)) == nullptr);
if (TypeTraits::IsSame<EntryType, Entry>::kValue)
{
printf("} (constructor-calls:%u, destructor-calls:%u)\n", sConstructorCalls, sDestructorCalls);
VerifyOrQuit(sConstructorCalls - sDestructorCalls == aArray.GetLength());
}
else
{
printf("}\n");
}
}
void TestHeapArrayOfUint16(void)
{
Heap::Array<uint16_t, 2> array;
Heap::Array<uint16_t, 2> array2;
uint16_t * entry;
printf("\n\n====================================================================================\n");
printf("TestHeapArrayOfUint16\n\n");
printf("------------------------------------------------------------------------------------\n");
printf("After constructor\n");
VerifyOrQuit(array.GetCapacity() == 0);
VerifyArray(array);
printf("------------------------------------------------------------------------------------\n");
printf("PushBack(aEntry)\n");
SuccessOrQuit(array.PushBack(1));
VerifyArray(array, 1);
VerifyOrQuit(array.GetCapacity() == 2);
SuccessOrQuit(array.PushBack(2));
VerifyArray(array, 1, 2);
VerifyOrQuit(array.GetCapacity() == 2);
SuccessOrQuit(array.PushBack(3));
VerifyArray(array, 1, 2, 3);
VerifyOrQuit(array.GetCapacity() == 4);
printf("------------------------------------------------------------------------------------\n");
printf("entry = PushBack()\n");
entry = array.PushBack();
VerifyOrQuit(entry != nullptr);
*entry = 4;
VerifyArray(array, 1, 2, 3, 4);
VerifyOrQuit(array.GetCapacity() == 4);
entry = array.PushBack();
VerifyOrQuit(entry != nullptr);
*entry = 5;
VerifyArray(array, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 6);
printf("------------------------------------------------------------------------------------\n");
printf("Clear()\n");
array.Clear();
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 6);
*array.PushBack() = 11;
SuccessOrQuit(array.PushBack(22));
SuccessOrQuit(array.PushBack(33));
SuccessOrQuit(array.PushBack(44));
*array.PushBack() = 55;
VerifyArray(array, 11, 22, 33, 44, 55);
VerifyOrQuit(array.GetCapacity() == 6);
SuccessOrQuit(array.PushBack(66));
SuccessOrQuit(array.PushBack(77));
VerifyArray(array, 11, 22, 33, 44, 55, 66, 77);
VerifyOrQuit(array.GetCapacity() == 8);
printf("------------------------------------------------------------------------------------\n");
printf("PopBack()\n");
array.PopBack();
VerifyArray(array, 11, 22, 33, 44, 55, 66);
VerifyOrQuit(array.GetCapacity() == 8);
array.PopBack();
array.PopBack();
array.PopBack();
array.PopBack();
array.PopBack();
VerifyArray(array, 11);
VerifyOrQuit(array.GetCapacity() == 8);
array.PopBack();
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 8);
array.PopBack();
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 8);
for (uint16_t num = 0; num < 11; num++)
{
SuccessOrQuit(array.PushBack(num + 0x100));
}
VerifyArray(array, 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10a);
VerifyOrQuit(array.GetCapacity() == 12);
printf("------------------------------------------------------------------------------------\n");
printf("Free()\n");
array.Free();
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 0);
array.Free();
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 0);
printf("------------------------------------------------------------------------------------\n");
printf("ReserveCapacity()\n");
SuccessOrQuit(array.ReserveCapacity(5));
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 5);
SuccessOrQuit(array.PushBack(0));
VerifyArray(array, 0);
VerifyOrQuit(array.GetCapacity() == 5);
for (uint16_t num = 1; num < 5; num++)
{
SuccessOrQuit(array.PushBack(num));
}
VerifyArray(array, 0, 1, 2, 3, 4);
VerifyOrQuit(array.GetCapacity() == 5);
SuccessOrQuit(array.PushBack(5));
VerifyArray(array, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 7);
SuccessOrQuit(array.ReserveCapacity(3));
VerifyArray(array, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 7);
SuccessOrQuit(array.ReserveCapacity(10));
VerifyArray(array, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 10);
printf("------------------------------------------------------------------------------------\n");
printf("TakeFrom()\n");
for (uint16_t num = 0; num < 7; num++)
{
SuccessOrQuit(array2.PushBack(num + 0x20));
}
VerifyArray(array2, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26);
array2.TakeFrom(static_cast<Heap::Array<uint16_t, 2> &&>(array));
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 0);
VerifyArray(array2, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array2.GetCapacity() == 10);
printf("\n -- PASS\n");
}
void TestHeapArray(void)
{
VerifyOrQuit(sConstructorCalls == 0);
VerifyOrQuit(sDestructorCalls == 0);
printf("\n\n====================================================================================\n");
printf("TestHeapArray\n\n");
{
Heap::Array<Entry, 2> array;
Heap::Array<Entry, 2> array2;
Entry * entry;
printf("------------------------------------------------------------------------------------\n");
printf("After constructor\n");
VerifyOrQuit(array.GetCapacity() == 0);
VerifyArray(array);
printf("------------------------------------------------------------------------------------\n");
printf("PushBack(aEntry)\n");
SuccessOrQuit(array.PushBack(Entry(1)));
VerifyArray(array, 1);
VerifyOrQuit(array.GetCapacity() == 2);
SuccessOrQuit(array.PushBack(Entry(2)));
VerifyArray(array, 1, 2);
VerifyOrQuit(array.GetCapacity() == 2);
SuccessOrQuit(array.PushBack(Entry(3)));
VerifyArray(array, 1, 2, 3);
VerifyOrQuit(array.GetCapacity() == 4);
entry = array.PushBack();
VerifyOrQuit(entry != nullptr);
VerifyOrQuit(entry->IsInitialized());
VerifyOrQuit(entry->GetValue() == 0);
entry->SetValue(4);
VerifyArray(array, 1, 2, 3, 4);
VerifyOrQuit(array.GetCapacity() == 4);
entry = array.PushBack();
VerifyOrQuit(entry != nullptr);
VerifyOrQuit(entry->IsInitialized());
VerifyOrQuit(entry->GetValue() == 0);
entry->SetValue(5);
VerifyArray(array, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 6);
printf("------------------------------------------------------------------------------------\n");
printf("PopBack()\n");
array.PopBack();
VerifyArray(array, 1, 2, 3, 4);
VerifyOrQuit(array.GetCapacity() == 6);
array.PopBack();
VerifyArray(array, 1, 2, 3);
VerifyOrQuit(array.GetCapacity() == 6);
SuccessOrQuit(array.PushBack(Entry(7)));
VerifyArray(array, 1, 2, 3, 7);
VerifyOrQuit(array.GetCapacity() == 6);
array.PopBack();
VerifyArray(array, 1, 2, 3);
VerifyOrQuit(array.GetCapacity() == 6);
printf("------------------------------------------------------------------------------------\n");
printf("Clear()\n");
array.Clear();
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 6);
for (uint16_t num = 0; num < 11; num++)
{
SuccessOrQuit(array.PushBack(Entry(num)));
}
VerifyArray(array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
VerifyOrQuit(array.GetCapacity() == 12);
printf("------------------------------------------------------------------------------------\n");
printf("Free()\n");
array.Free();
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 0);
printf("------------------------------------------------------------------------------------\n");
printf("ReserveCapacity()\n");
SuccessOrQuit(array.ReserveCapacity(5));
VerifyArray(array);
VerifyOrQuit(array.GetCapacity() == 5);
SuccessOrQuit(array.PushBack(Entry(0)));
VerifyArray(array, 0);
VerifyOrQuit(array.GetCapacity() == 5);
for (uint16_t num = 1; num < 5; num++)
{
SuccessOrQuit(array.PushBack(Entry(num)));
}
VerifyArray(array, 0, 1, 2, 3, 4);
VerifyOrQuit(array.GetCapacity() == 5);
SuccessOrQuit(array.PushBack(Entry(5)));
VerifyArray(array, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 7);
SuccessOrQuit(array.ReserveCapacity(3));
VerifyArray(array, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 7);
SuccessOrQuit(array.ReserveCapacity(10));
VerifyArray(array, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array.GetCapacity() == 10);
printf("------------------------------------------------------------------------------------\n");
printf("TakeFrom()\n");
for (uint16_t num = 0; num < 7; num++)
{
SuccessOrQuit(array2.PushBack(Entry(num + 0x20)));
}
array2.TakeFrom(static_cast<Heap::Array<Entry, 2> &&>(array));
VerifyOrQuit(array.GetLength() == 0);
VerifyOrQuit(array.GetCapacity() == 0);
VerifyArray(array2, 0, 1, 2, 3, 4, 5);
VerifyOrQuit(array2.GetCapacity() == 10);
}
printf("------------------------------------------------------------------------------------\n");
printf("Array destructor\n");
printf(" - (constructor-calls:%u, destructor-calls:%u)\n", sConstructorCalls, sDestructorCalls);
VerifyOrQuit(sConstructorCalls == sDestructorCalls,
"Array destructor failed to invoke destructor on all its existing entries");
printf("\n -- PASS\n");
}
} // namespace ot
int main(void)
{
ot::TestHeapArrayOfUint16();
ot::TestHeapArray();
printf("\nAll tests passed.\n");
return 0;
}