• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 template <typename Type, uint16_t kCapacityIncrements = 2> class Array
66 {
67 public:
68     using IndexType = uint16_t;
69 
70     /**
71      * Initializes the `Array` as empty.
72      */
Array(void)73     Array(void)
74         : mArray(nullptr)
75         , mLength(0)
76         , mCapacity(0)
77     {
78     }
79 
80     /**
81      * This is the destructor for `Array` object.
82      */
~Array(void)83     ~Array(void) { Free(); }
84 
85     /**
86      * Frees any buffer allocated by the `Array`.
87      *
88      * The `Array` destructor will automatically call `Free()`. This method allows caller to free buffer explicitly.
89      */
Free(void)90     void Free(void)
91     {
92         Clear();
93         Heap::Free(mArray);
94         mArray    = nullptr;
95         mCapacity = 0;
96     }
97 
98     /**
99      * Clears the array.
100      *
101      * Note that `Clear()` method (unlike `Free()`) does not free the allocated buffer and therefore does not change
102      * the current capacity of the array.
103      *
104      * Invokes `Type` destructor on all cleared existing elements of array.
105      */
Clear(void)106     void Clear(void)
107     {
108         for (Type &entry : *this)
109         {
110             entry.~Type();
111         }
112 
113         mLength = 0;
114     }
115 
116     /**
117      * Returns the current array length (number of elements in the array).
118      *
119      * @returns The array length.
120      */
GetLength(void) const121     IndexType GetLength(void) const { return mLength; }
122 
123     /**
124      * Returns a raw pointer to the array buffer.
125      *
126      * The returned raw pointer is valid only while the `Array` remains unchanged.
127      *
128      * @returns A pointer to the array buffer or `nullptr` if the array is empty.
129      */
AsCArray(void) const130     const Type *AsCArray(void) const { return (mLength != 0) ? mArray : nullptr; }
131 
132     /**
133      * Returns the current capacity of array (number of elements that can fit in current allocated buffer).
134      *
135      * The allocated buffer and array capacity are automatically increased (by the `Array` itself) when new elements
136      * are added to array. Removing elements does not change the buffer and the capacity. A desired capacity can be
137      * reserved using `ReserveCapacity()` method.
138      *
139      * @returns The current capacity of the array.
140      */
GetCapacity(void) const141     IndexType GetCapacity(void) const { return mCapacity; }
142 
143     /**
144      * Allocates buffer to reserve a given capacity for array.
145      *
146      * If the requested @p aCapacity is smaller than the current length of the array, capacity remains unchanged.
147      *
148      * @param[in] aCapacity   The target capacity for the array.
149      *
150      * @retval kErrorNone     Array was successfully updated to support @p aCapacity.
151      * @retval kErrorNoBufs   Could not allocate buffer.
152      */
ReserveCapacity(IndexType aCapacity)153     Error ReserveCapacity(IndexType aCapacity) { return Allocate(aCapacity); }
154 
155     /**
156      * Sets the array by taking the buffer from another given array (using move semantics).
157      *
158      * @param[in] aOther    The other `Heap::Array` to take from (rvalue reference).
159      */
TakeFrom(Array && aOther)160     void TakeFrom(Array &&aOther)
161     {
162         Free();
163         mArray           = aOther.mArray;
164         mLength          = aOther.mLength;
165         mCapacity        = aOther.mCapacity;
166         aOther.mArray    = nullptr;
167         aOther.mLength   = 0;
168         aOther.mCapacity = 0;
169     }
170 
171     /**
172      * Overloads the `[]` operator to get the element at a given index.
173      *
174      * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
175      *
176      * @param[in] aIndex  The index to get.
177      *
178      * @returns A reference to the element in array at @p aIndex.
179      */
operator [](IndexType aIndex)180     Type &operator[](IndexType aIndex) { return mArray[aIndex]; }
181 
182     /**
183      * Overloads the `[]` operator to get the element at a given index.
184      *
185      * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
186      *
187      * @param[in] aIndex  The index to get.
188      *
189      * @returns A reference to the element in array at @p aIndex.
190      */
operator [](IndexType aIndex) const191     const Type &operator[](IndexType aIndex) const { return mArray[aIndex]; }
192 
193     /**
194      * Gets a pointer to the element at a given index.
195      *
196      * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
197      * pointer is valid only while the `Array` remains unchanged.
198      *
199      * @param[in] aIndex  The index to get.
200      *
201      * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
202      */
At(IndexType aIndex)203     Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
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      */
At(IndexType aIndex) const215     const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
216 
217     /**
218      * Gets a pointer to the element at the front of the array (first element).
219      *
220      * The returned pointer is valid only while the `Array` remains unchanged.
221      *
222      * @returns A pointer to the front element or `nullptr` if array is empty.
223      */
Front(void)224     Type *Front(void) { return At(0); }
225 
226     /**
227      * Gets a pointer to the element at the front of the array (first element).
228      *
229      * The returned pointer is valid only while the `Array` remains unchanged.
230      *
231      * @returns A pointer to the front element or `nullptr` if array is empty.
232      */
Front(void) const233     const Type *Front(void) const { return At(0); }
234 
235     /**
236      * Gets a pointer to the element at the back of the array (last element).
237      *
238      * The returned pointer is valid only while the `Array` remains unchanged.
239      *
240      * @returns A pointer to the back element or `nullptr` if array is empty.
241      */
Back(void)242     Type *Back(void) { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
243 
244     /**
245      * Gets a pointer to the element at the back of the array (last element).
246      *
247      * The returned pointer is valid only while the `Array` remains unchanged.
248      *
249      * @returns A pointer to the back element or `nullptr` if array is empty.
250      */
Back(void) const251     const Type *Back(void) const { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
252 
253     /**
254      * Appends a new entry to the end of the array.
255      *
256      * Requires the `Type` to provide a copy constructor of format `Type(const Type &aOther)` to init the
257      * new element in the array from @p aEntry.
258      *
259      * @param[in] aEntry     The new entry to push back.
260      *
261      * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
262      * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
263      */
PushBack(const Type & aEntry)264     Error PushBack(const Type &aEntry)
265     {
266         Error error = kErrorNone;
267 
268         if (mLength == mCapacity)
269         {
270             SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
271         }
272 
273         new (&mArray[mLength++]) Type(aEntry);
274 
275     exit:
276         return error;
277     }
278 
279     /**
280      * Appends a new entry to the end of the array.
281      *
282      * Requires the `Type` to provide a copy constructor of format `Type(Type &&aOther)` to init the
283      * new element in the array from @p aEntry.
284      *
285      * @param[in] aEntry     The new entry to push back (an rvalue reference)
286      *
287      * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
288      * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
289      */
PushBack(Type && aEntry)290     Error PushBack(Type &&aEntry)
291     {
292         Error error = kErrorNone;
293 
294         if (mLength == mCapacity)
295         {
296             SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
297         }
298 
299         new (&mArray[mLength++]) Type(static_cast<Type &&>(aEntry));
300 
301     exit:
302         return error;
303     }
304 
305     /**
306      * Appends a new entry to the end of the array.
307      *
308      * On success, this method returns a pointer to the newly appended element in the array for the caller to
309      * initialize and use. This method uses the `Type(void)` default constructor on the newly appended element (if not
310      * `nullptr`).
311      *
312      * The returned pointer is valid only while the `Array` remains unchanged.
313      *
314      * @return A pointer to the newly appended element or `nullptr` if could not allocate buffer to grow the array
315      */
PushBack(void)316     Type *PushBack(void)
317     {
318         Type *newEntry = nullptr;
319 
320         if (mLength == mCapacity)
321         {
322             SuccessOrExit(Allocate(mCapacity + kCapacityIncrements));
323         }
324 
325         newEntry = new (&mArray[mLength++]) Type();
326 
327     exit:
328         return newEntry;
329     }
330 
331     /**
332      * Removes the last element in the array.
333      *
334      * Will invoke the `Type` destructor on the removed element.
335      */
PopBack(void)336     void PopBack(void)
337     {
338         if (mLength > 0)
339         {
340             mArray[mLength - 1].~Type();
341             mLength--;
342         }
343     }
344 
345     /**
346      * Returns the index of an element in the array.
347      *
348      * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
349      *
350      * @param[in] aElement  A reference to an element in the array.
351      *
352      * @returns The index of @p aElement in the array.
353      */
IndexOf(const Type & aElement) const354     IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - mArray); }
355 
356     /**
357      * Finds the first match of a given entry in the array.
358      *
359      * Uses `==` operator on `Type` to compare the array element with @p aEntry. The returned pointer is
360      * valid only while the `Array` remains unchanged.
361      *
362      * @param[in] aEntry   The entry to search for within the array.
363      *
364      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
365      */
Find(const Type & aEntry)366     Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); }
367 
368     /**
369      * Finds the first match of a given entry in the array.
370      *
371      * Uses `==` operator to compare the array elements with @p aEntry. The returned pointer is valid only
372      * while the `Array` remains unchanged.
373      *
374      * @param[in] aEntry   The entry to search for within the array.
375      *
376      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
377      */
Find(const Type & aEntry) const378     const Type *Find(const Type &aEntry) const
379     {
380         const Type *matched = nullptr;
381 
382         for (const Type &element : *this)
383         {
384             if (element == aEntry)
385             {
386                 matched = &element;
387                 break;
388             }
389         }
390 
391         return matched;
392     }
393 
394     /**
395      * Indicates whether or not a match to given entry exists in the array.
396      *
397      * Uses `==` operator on `Type` to compare the array elements with @p aEntry.
398      *
399      * @param[in] aEntry   The entry to search for within the array.
400      *
401      * @retval TRUE   The array contains a matching element with @p aEntry.
402      * @retval FALSE  The array does not contain a matching element with @p aEntry.
403      */
Contains(const Type & aEntry) const404     bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
405 
406     /**
407      * Finds the first element in the array matching a given indicator.
408      *
409      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
410      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
411      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
412      *
413      *     bool Type::Matches(const Indicator &aIndicator) const
414      *
415      * The returned pointer is valid only while the `Array` remains unchanged.
416      *
417      * @param[in]  aIndicator  An indicator to match with elements in the array.
418      *
419      * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
420      */
FindMatching(const Indicator & aIndicator)421     template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
422     {
423         return AsNonConst(AsConst(this)->FindMatching(aIndicator));
424     }
425 
426     /**
427      * Finds the first element in the array matching a given indicator.
428      *
429      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
430      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
431      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
432      *
433      *     bool Type::Matches(const Indicator &aIndicator) const
434      *
435      * The returned pointer is valid only while the `Array` remains unchanged.
436      *
437      * @param[in]  aIndicator  An indicator to match with elements in the array.
438      *
439      * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
440      */
FindMatching(const Indicator & aIndicator) const441     template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
442     {
443         const Type *matched = nullptr;
444 
445         for (const Type &element : *this)
446         {
447             if (element.Matches(aIndicator))
448             {
449                 matched = &element;
450                 break;
451             }
452         }
453 
454         return matched;
455     }
456 
457     /**
458      * Indicates whether or not the array contains an element matching a given indicator.
459      *
460      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
461      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
462      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
463      *
464      *     bool Type::Matches(const Indicator &aIndicator) const
465      *
466      * @param[in]  aIndicator  An indicator to match with elements in the array.
467      *
468      * @retval TRUE   The array contains a matching element with @p aIndicator.
469      * @retval FALSE  The array does not contain a matching element with @p aIndicator.
470      */
ContainsMatching(const Indicator & aIndicator) const471     template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
472     {
473         return FindMatching(aIndicator) != nullptr;
474     }
475 
476     // The following methods are intended to support range-based `for`
477     // loop iteration over the array elements and should not be used
478     // directly.
479 
begin(void)480     Type       *begin(void) { return (mLength > 0) ? mArray : nullptr; }
end(void)481     Type       *end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; }
begin(void) const482     const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; }
end(void) const483     const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; }
484 
485     Array(const Array &)            = delete;
486     Array &operator=(const Array &) = delete;
487 
488 private:
Allocate(IndexType aCapacity)489     Error Allocate(IndexType aCapacity)
490     {
491         Error error = kErrorNone;
492         Type *newArray;
493 
494         VerifyOrExit((aCapacity != mCapacity) && (aCapacity >= mLength));
495         newArray = static_cast<Type *>(Heap::CAlloc(aCapacity, sizeof(Type)));
496         VerifyOrExit(newArray != nullptr, error = kErrorNoBufs);
497 
498         for (IndexType index = 0; index < mLength; index++)
499         {
500             new (&newArray[index]) Type(static_cast<Type &&>(mArray[index]));
501             mArray[index].~Type();
502         }
503 
504         Heap::Free(mArray);
505         mArray    = newArray;
506         mCapacity = aCapacity;
507 
508     exit:
509         return error;
510     }
511 
512     Type     *mArray;
513     IndexType mLength;
514     IndexType mCapacity;
515 };
516 
517 } // namespace Heap
518 } // namespace ot
519 
520 #endif // HEAP_ARRAY_HPP_
521