xref: /aosp_15_r20/external/openthread/src/core/common/heap_array.hpp (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1 /*
2  *  Copyright (c) 2022, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file includes definitions for `Heap::Array` (a heap allocated array of flexible length).
32  */
33 
34 #ifndef HEAP_ARRAY_HPP_
35 #define HEAP_ARRAY_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #include <stdint.h>
40 #include <stdio.h>
41 
42 #include "common/array.hpp"
43 #include "common/code_utils.hpp"
44 #include "common/error.hpp"
45 #include "common/heap.hpp"
46 #include "common/new.hpp"
47 
48 namespace ot {
49 namespace Heap {
50 
51 /**
52  * Represents a heap allocated array.
53  *
54  * The buffer to store the elements is allocated from heap and is managed by the `Heap::Array` class itself. The `Array`
55  * implementation will automatically grow the buffer when new entries are added. The `Heap::Array` destructor will
56  * always free the allocated buffer.
57  *
58  * The `Type` class MUST provide a move constructor `Type(Type &&aOther)` (or a copy constructor if no move constructor
59  * is provided). This constructor is used to move existing elements when array buffer is grown (new buffer is
60  * allocated) to make room for new elements.
61  *
62  * @tparam  Type                  The array element type.
63  * @tparam  kCapacityIncrements   Number of elements to allocate at a time when updating the array buffer.
64  *
65  */
66 template <typename Type, uint16_t kCapacityIncrements = 2> class Array
67 {
68 public:
69     using IndexType = uint16_t;
70 
71     /**
72      * Initializes the `Array` as empty.
73      *
74      */
Array(void)75     Array(void)
76         : mArray(nullptr)
77         , mLength(0)
78         , mCapacity(0)
79     {
80     }
81 
82     /**
83      * This is the destructor for `Array` object.
84      *
85      */
~Array(void)86     ~Array(void) { Free(); }
87 
88     /**
89      * Frees any buffer allocated by the `Array`.
90      *
91      * The `Array` destructor will automatically call `Free()`. This method allows caller to free buffer explicitly.
92      *
93      */
Free(void)94     void Free(void)
95     {
96         Clear();
97         Heap::Free(mArray);
98         mArray    = nullptr;
99         mCapacity = 0;
100     }
101 
102     /**
103      * Clears the array.
104      *
105      * Note that `Clear()` method (unlike `Free()`) does not free the allocated buffer and therefore does not change
106      * the current capacity of the array.
107      *
108      * Invokes `Type` destructor on all cleared existing elements of array.
109      *
110      */
Clear(void)111     void Clear(void)
112     {
113         for (Type &entry : *this)
114         {
115             entry.~Type();
116         }
117 
118         mLength = 0;
119     }
120 
121     /**
122      * Returns the current array length (number of elements in the array).
123      *
124      * @returns The array length.
125      *
126      */
GetLength(void) const127     IndexType GetLength(void) const { return mLength; }
128 
129     /**
130      * Returns a raw pointer to the array buffer.
131      *
132      * The returned raw pointer is valid only while the `Array` remains unchanged.
133      *
134      * @returns A pointer to the array buffer or `nullptr` if the array is empty.
135      *
136      */
AsCArray(void) const137     const Type *AsCArray(void) const { return (mLength != 0) ? mArray : nullptr; }
138 
139     /**
140      * Returns the current capacity of array (number of elements that can fit in current allocated buffer).
141      *
142      * The allocated buffer and array capacity are automatically increased (by the `Array` itself) when new elements
143      * are added to array. Removing elements does not change the buffer and the capacity. A desired capacity can be
144      * reserved using `ReserveCapacity()` method.
145      *
146      * @returns The current capacity of the array.
147      *
148      */
GetCapacity(void) const149     IndexType GetCapacity(void) const { return mCapacity; }
150 
151     /**
152      * Allocates buffer to reserve a given capacity for array.
153      *
154      * If the requested @p aCapacity is smaller than the current length of the array, capacity remains unchanged.
155      *
156      * @param[in] aCapacity   The target capacity for the array.
157      *
158      * @retval kErrorNone     Array was successfully updated to support @p aCapacity.
159      * @retval kErrorNoBufs   Could not allocate buffer.
160      *
161      */
ReserveCapacity(IndexType aCapacity)162     Error ReserveCapacity(IndexType aCapacity) { return Allocate(aCapacity); }
163 
164     /**
165      * Sets the array by taking the buffer from another given array (using move semantics).
166      *
167      * @param[in] aOther    The other `Heap::Array` to take from (rvalue reference).
168      *
169      */
TakeFrom(Array && aOther)170     void TakeFrom(Array &&aOther)
171     {
172         Free();
173         mArray           = aOther.mArray;
174         mLength          = aOther.mLength;
175         mCapacity        = aOther.mCapacity;
176         aOther.mArray    = nullptr;
177         aOther.mLength   = 0;
178         aOther.mCapacity = 0;
179     }
180 
181     /**
182      * Overloads the `[]` operator to get the element at a given index.
183      *
184      * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
185      *
186      * @param[in] aIndex  The index to get.
187      *
188      * @returns A reference to the element in array at @p aIndex.
189      *
190      */
operator [](IndexType aIndex)191     Type &operator[](IndexType aIndex) { return mArray[aIndex]; }
192 
193     /**
194      * Overloads the `[]` operator to get the element at a given index.
195      *
196      * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
197      *
198      * @param[in] aIndex  The index to get.
199      *
200      * @returns A reference to the element in array at @p aIndex.
201      *
202      */
operator [](IndexType aIndex) const203     const Type &operator[](IndexType aIndex) const { return mArray[aIndex]; }
204 
205     /**
206      * Gets a pointer to the element at a given index.
207      *
208      * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
209      * pointer is valid only while the `Array` remains unchanged.
210      *
211      * @param[in] aIndex  The index to get.
212      *
213      * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
214      *
215      */
At(IndexType aIndex)216     Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
217 
218     /**
219      * Gets a pointer to the element at a given index.
220      *
221      * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
222      * pointer is valid only while the `Array` remains unchanged.
223      *
224      * @param[in] aIndex  The index to get.
225      *
226      * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
227      *
228      */
At(IndexType aIndex) const229     const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
230 
231     /**
232      * Gets a pointer to the element at the front of the array (first element).
233      *
234      * The returned pointer is valid only while the `Array` remains unchanged.
235      *
236      * @returns A pointer to the front element or `nullptr` if array is empty.
237      *
238      */
Front(void)239     Type *Front(void) { return At(0); }
240 
241     /**
242      * Gets a pointer to the element at the front of the array (first element).
243      *
244      * The returned pointer is valid only while the `Array` remains unchanged.
245      *
246      * @returns A pointer to the front element or `nullptr` if array is empty.
247      *
248      */
Front(void) const249     const Type *Front(void) const { return At(0); }
250 
251     /**
252      * Gets a pointer to the element at the back of the array (last element).
253      *
254      * The returned pointer is valid only while the `Array` remains unchanged.
255      *
256      * @returns A pointer to the back element or `nullptr` if array is empty.
257      *
258      */
Back(void)259     Type *Back(void) { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
260 
261     /**
262      * Gets a pointer to the element at the back of the array (last element).
263      *
264      * The returned pointer is valid only while the `Array` remains unchanged.
265      *
266      * @returns A pointer to the back element or `nullptr` if array is empty.
267      *
268      */
Back(void) const269     const Type *Back(void) const { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
270 
271     /**
272      * Appends a new entry to the end of the array.
273      *
274      * Requires the `Type` to provide a copy constructor of format `Type(const Type &aOther)` to init the
275      * new element in the array from @p aEntry.
276      *
277      * @param[in] aEntry     The new entry to push back.
278      *
279      * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
280      * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
281      *
282      */
PushBack(const Type & aEntry)283     Error PushBack(const Type &aEntry)
284     {
285         Error error = kErrorNone;
286 
287         if (mLength == mCapacity)
288         {
289             SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
290         }
291 
292         new (&mArray[mLength++]) Type(aEntry);
293 
294     exit:
295         return error;
296     }
297 
298     /**
299      * Appends a new entry to the end of the array.
300      *
301      * Requires the `Type` to provide a copy constructor of format `Type(Type &&aOther)` to init the
302      * new element in the array from @p aEntry.
303      *
304      * @param[in] aEntry     The new entry to push back (an rvalue reference)
305      *
306      * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
307      * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
308      *
309      */
PushBack(Type && aEntry)310     Error PushBack(Type &&aEntry)
311     {
312         Error error = kErrorNone;
313 
314         if (mLength == mCapacity)
315         {
316             SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
317         }
318 
319         new (&mArray[mLength++]) Type(static_cast<Type &&>(aEntry));
320 
321     exit:
322         return error;
323     }
324 
325     /**
326      * Appends a new entry to the end of the array.
327      *
328      * On success, this method returns a pointer to the newly appended element in the array for the caller to
329      * initialize and use. This method uses the `Type(void)` default constructor on the newly appended element (if not
330      * `nullptr`).
331      *
332      * The returned pointer is valid only while the `Array` remains unchanged.
333      *
334      * @return A pointer to the newly appended element or `nullptr` if could not allocate buffer to grow the array
335      *
336      */
PushBack(void)337     Type *PushBack(void)
338     {
339         Type *newEntry = nullptr;
340 
341         if (mLength == mCapacity)
342         {
343             SuccessOrExit(Allocate(mCapacity + kCapacityIncrements));
344         }
345 
346         newEntry = new (&mArray[mLength++]) Type();
347 
348     exit:
349         return newEntry;
350     }
351 
352     /**
353      * Removes the last element in the array.
354      *
355      * Will invoke the `Type` destructor on the removed element.
356      *
357      */
PopBack(void)358     void PopBack(void)
359     {
360         if (mLength > 0)
361         {
362             mArray[mLength - 1].~Type();
363             mLength--;
364         }
365     }
366 
367     /**
368      * Returns the index of an element in the array.
369      *
370      * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
371      *
372      * @param[in] aElement  A reference to an element in the array.
373      *
374      * @returns The index of @p aElement in the array.
375      *
376      */
IndexOf(const Type & aElement) const377     IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - mArray); }
378 
379     /**
380      * Finds the first match of a given entry in the array.
381      *
382      * Uses `==` operator on `Type` to compare the array element with @p aEntry. The returned pointer is
383      * valid only while the `Array` remains unchanged.
384      *
385      * @param[in] aEntry   The entry to search for within the array.
386      *
387      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
388      *
389      */
Find(const Type & aEntry)390     Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); }
391 
392     /**
393      * Finds the first match of a given entry in the array.
394      *
395      * Uses `==` operator to compare the array elements with @p aEntry. The returned pointer is valid only
396      * while the `Array` remains unchanged.
397      *
398      * @param[in] aEntry   The entry to search for within the array.
399      *
400      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
401      *
402      */
Find(const Type & aEntry) const403     const Type *Find(const Type &aEntry) const
404     {
405         const Type *matched = nullptr;
406 
407         for (const Type &element : *this)
408         {
409             if (element == aEntry)
410             {
411                 matched = &element;
412                 break;
413             }
414         }
415 
416         return matched;
417     }
418 
419     /**
420      * Indicates whether or not a match to given entry exists in the array.
421      *
422      * Uses `==` operator on `Type` to compare the array elements with @p aEntry.
423      *
424      * @param[in] aEntry   The entry to search for within the array.
425      *
426      * @retval TRUE   The array contains a matching element with @p aEntry.
427      * @retval FALSE  The array does not contain a matching element with @p aEntry.
428      *
429      */
Contains(const Type & aEntry) const430     bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
431 
432     /**
433      * Finds the first element in the array matching a given indicator.
434      *
435      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
436      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
437      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
438      *
439      *     bool Type::Matches(const Indicator &aIndicator) const
440      *
441      * The returned pointer is valid only while the `Array` remains unchanged.
442      *
443      * @param[in]  aIndicator  An indicator to match with elements in the array.
444      *
445      * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
446      *
447      */
FindMatching(const Indicator & aIndicator)448     template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
449     {
450         return AsNonConst(AsConst(this)->FindMatching(aIndicator));
451     }
452 
453     /**
454      * Finds the first element in the array matching a given indicator.
455      *
456      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
457      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
458      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
459      *
460      *     bool Type::Matches(const Indicator &aIndicator) const
461      *
462      * The returned pointer is valid only while the `Array` remains unchanged.
463      *
464      * @param[in]  aIndicator  An indicator to match with elements in the array.
465      *
466      * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
467      *
468      */
FindMatching(const Indicator & aIndicator) const469     template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
470     {
471         const Type *matched = nullptr;
472 
473         for (const Type &element : *this)
474         {
475             if (element.Matches(aIndicator))
476             {
477                 matched = &element;
478                 break;
479             }
480         }
481 
482         return matched;
483     }
484 
485     /**
486      * Indicates whether or not the array contains an element matching a given indicator.
487      *
488      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
489      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
490      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
491      *
492      *     bool Type::Matches(const Indicator &aIndicator) const
493      *
494      * @param[in]  aIndicator  An indicator to match with elements in the array.
495      *
496      * @retval TRUE   The array contains a matching element with @p aIndicator.
497      * @retval FALSE  The array does not contain a matching element with @p aIndicator.
498      *
499      */
ContainsMatching(const Indicator & aIndicator) const500     template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
501     {
502         return FindMatching(aIndicator) != nullptr;
503     }
504 
505     // The following methods are intended to support range-based `for`
506     // loop iteration over the array elements and should not be used
507     // directly.
508 
begin(void)509     Type       *begin(void) { return (mLength > 0) ? mArray : nullptr; }
end(void)510     Type       *end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; }
begin(void) const511     const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; }
end(void) const512     const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; }
513 
514     Array(const Array &)            = delete;
515     Array &operator=(const Array &) = delete;
516 
517 private:
Allocate(IndexType aCapacity)518     Error Allocate(IndexType aCapacity)
519     {
520         Error error = kErrorNone;
521         Type *newArray;
522 
523         VerifyOrExit((aCapacity != mCapacity) && (aCapacity >= mLength));
524         newArray = static_cast<Type *>(Heap::CAlloc(aCapacity, sizeof(Type)));
525         VerifyOrExit(newArray != nullptr, error = kErrorNoBufs);
526 
527         for (IndexType index = 0; index < mLength; index++)
528         {
529             new (&newArray[index]) Type(static_cast<Type &&>(mArray[index]));
530             mArray[index].~Type();
531         }
532 
533         Heap::Free(mArray);
534         mArray    = newArray;
535         mCapacity = aCapacity;
536 
537     exit:
538         return error;
539     }
540 
541     Type     *mArray;
542     IndexType mLength;
543     IndexType mCapacity;
544 };
545 
546 } // namespace Heap
547 } // namespace ot
548 
549 #endif // HEAP_ARRAY_HPP_
550