mirror of
https://github.com/espressif/openthread.git
synced 2026-08-04 01:47:47 +00:00
[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:
committed by
Jonathan Hui
parent
465518ec64
commit
4b31f57014
@@ -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",
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user