blob: a4ede9027ca687dbc2f1154fc3d2bb2b92551976 [file] [log] [blame]
/*
* 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.
*
*/
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_