• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2021, 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 a generic array.
32  */
33 
34 #ifndef ARRAY_HPP_
35 #define ARRAY_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #include "common/code_utils.hpp"
40 #include "common/const_cast.hpp"
41 #include "common/error.hpp"
42 #include "common/numeric_limits.hpp"
43 #include "common/type_traits.hpp"
44 
45 namespace ot {
46 
47 /**
48  * This function returns the length of a given array (number of elements in the array).
49  *
50  * This template function is `constexpr`. The template arguments are expected to be deduced by the compiler allowing
51  * callers to simply use `GetArrayLength(aArray)`.
52  *
53  * @tparam  Type          The array element type.
54  * @tparam  kArrayLength  The array length.
55  *
56  * @returns The array length (number of elements in the array).
57  *
58  */
GetArrayLength(const Type (&)[kArrayLength])59 template <typename Type, uint16_t kArrayLength> constexpr inline uint16_t GetArrayLength(const Type (&)[kArrayLength])
60 {
61     return kArrayLength;
62 }
63 
64 /**
65  * This function returns a pointer to end of a given array (pointing to the past-the-end element).
66  *
67  * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
68  * not point to an actual element in array, and thus should not be dereferenced.
69  *
70  * @tparam  Type          The array element type.
71  * @tparam  kArrayLength  The array length.
72  *
73  * @param[in] aArray   A reference to the array.
74  *
75  * @returns Pointer to the past-the-end element.
76  *
77  */
GetArrayEnd(Type (& aArray)[kArrayLength])78 template <typename Type, uint16_t kArrayLength> inline Type *GetArrayEnd(Type (&aArray)[kArrayLength])
79 {
80     return &aArray[kArrayLength];
81 }
82 
83 /**
84  * This function returns a pointer to end of a given array (pointing to the past-the-end element).
85  *
86  * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
87  * not point to an actual element in array, and thus should not be dereferenced.
88  *
89  * @tparam  Type          The array element type.
90  * @tparam  kArrayLength  The array length.
91  *
92  * @param[in] aArray   A reference to the array.
93  *
94  * @returns Pointer to the past-the-end element.
95  *
96  */
GetArrayEnd(const Type (& aArray)[kArrayLength])97 template <typename Type, uint16_t kArrayLength> inline const Type *GetArrayEnd(const Type (&aArray)[kArrayLength])
98 {
99     return &aArray[kArrayLength];
100 }
101 
102 /**
103  * This template class represents an array of elements with a fixed max size.
104  *
105  * @tparam Type        The array element type.
106  * @tparam kMaxSize    Specifies the max array size (maximum number of elements in the array).
107  * @tparam SizeType    The type to be used for array size, length, and index. If not specified, a default `uint` type
108  *                     is determined based on `kMaxSize`, i.e., if `kMaxSize <= 255` then `uint8_t` will be used,
109  *                     otherwise `uint16_t` will be used.
110  *
111  */
112 template <typename Type,
113           uint16_t kMaxSize,
114           typename SizeType =
115               typename TypeTraits::Conditional<kMaxSize <= NumericLimits<uint8_t>::kMax, uint8_t, uint16_t>::Type>
116 class Array
117 {
118     static_assert(kMaxSize != 0, "Array `kMaxSize` cannot be zero");
119 
120 public:
121     /**
122      * This type represents the length or index in array.
123      *
124      * It is typically either `uint8_t` or `uint16_t` (determined based on the maximum array size (`kMaxSize`)).
125      *
126      */
127     typedef SizeType IndexType;
128 
129     /**
130      * This constructor initializes the array as empty.
131      *
132      */
Array(void)133     Array(void)
134         : mLength(0)
135     {
136     }
137 
138     /**
139      * This constructor initializes the array by copying elements from another array.
140      *
141      * The method uses assignment `=` operator on `Type` to copy each element from @p aOtherArray into the elements of
142      * the array.
143      *
144      * @param[in] aOtherArray  Another array to copy from.
145      *
146      */
Array(const Array & aOtherArray)147     Array(const Array &aOtherArray) { *this = aOtherArray; }
148 
149     /**
150      * This method clears the array.
151      *
152      */
Clear(void)153     void Clear(void) { mLength = 0; }
154 
155     /**
156      * This method indicates whether or not the array is empty.
157      *
158      * @retval TRUE when array is empty.
159      * @retval FALSE when array is not empty.
160      *
161      */
IsEmpty(void) const162     bool IsEmpty(void) const { return (mLength == 0); }
163 
164     /**
165      * This method indicates whether or not the array is full.
166      *
167      * @retval TRUE when array is full.
168      * @retval FALSE when array is not full.
169      *
170      */
IsFull(void) const171     bool IsFull(void) const { return (mLength == GetMaxSize()); }
172 
173     /**
174      * This method returns the maximum array size (max number of elements).
175      *
176      * @returns The maximum array size (max number of elements that can be added to the array).
177      *
178      */
GetMaxSize(void) const179     IndexType GetMaxSize(void) const { return static_cast<IndexType>(kMaxSize); }
180 
181     /**
182      * This method returns the current length of array (number of elements).
183      *
184      * @returns The current array length.
185      *
186      */
GetLength(void) const187     IndexType GetLength(void) const { return mLength; }
188 
189     /**
190      * This method overloads the `[]` operator to get the element at a given index.
191      *
192      * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
193      *
194      * @param[in] aIndex  The index to get.
195      *
196      * @returns A reference to the element in array at @p aIndex.
197      *
198      */
operator [](IndexType aIndex)199     Type &operator[](IndexType aIndex) { return mElements[aIndex]; }
200 
201     /**
202      * This method overloads the `[]` operator to get the element at a given index.
203      *
204      * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
205      *
206      * @param[in] aIndex  The index to get.
207      *
208      * @returns A reference to the element in array at @p aIndex.
209      *
210      */
operator [](IndexType aIndex) const211     const Type &operator[](IndexType aIndex) const { return mElements[aIndex]; }
212 
213     /**
214      * This method gets a pointer to the element at a given index.
215      *
216      * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length.
217      *
218      * @param[in] aIndex  The index to get.
219      *
220      * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
221      *
222      */
At(IndexType aIndex)223     Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mElements[aIndex] : nullptr; }
224 
225     /**
226      * This method gets a pointer to the element at a given index.
227      *
228      * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length.
229      *
230      * @param[in] aIndex  The index to get.
231      *
232      * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
233      *
234      */
At(IndexType aIndex) const235     const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mElements[aIndex] : nullptr; }
236 
237     /**
238      * This method gets a pointer to the element at the front of the array (first element).
239      *
240      * @returns A pointer to the front element or `nullptr` if array is empty.
241      *
242      */
Front(void)243     Type *Front(void) { return At(0); }
244 
245     /**
246      * This method gets a pointer to the element at the front of the array (first element).
247      *
248      * @returns A pointer to the front element or `nullptr` if array is empty.
249      *
250      */
Front(void) const251     const Type *Front(void) const { return At(0); }
252 
253     /**
254      * This method gets a pointer to the element at the back of the array (last element).
255      *
256      * @returns A pointer to the back element or `nullptr` if array is empty.
257      *
258      */
Back(void)259     Type *Back(void) { return At(mLength - 1); }
260 
261     /**
262      * This method gets a pointer to the element at the back of the array (last element).
263      *
264      * @returns A pointer to the back element or `nullptr` if array is empty.
265      *
266      */
Back(void) const267     const Type *Back(void) const { return At(mLength - 1); }
268 
269     /**
270      * This method appends a new entry to the end of the array.
271      *
272      * The method uses assignment `=` operator on `Type` to copy @p aEntry into the added array element.
273      *
274      * @param[in] aEntry     The new entry to push back.
275      *
276      * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
277      * @retval kErrorNoBufs  Could not append the new element since array is full.
278      *
279      */
PushBack(const Type & aEntry)280     Error PushBack(const Type &aEntry) { return IsFull() ? kErrorNoBufs : (mElements[mLength++] = aEntry, kErrorNone); }
281 
282     /**
283      * This method appends a new entry to the end of the array.
284      *
285      * On success, this method returns a pointer to the newly appended element in the array for the caller to
286      * initialize and use.
287      *
288      * @return A pointer to the newly appended element or `nullptr` if array is full.
289      *
290      */
PushBack(void)291     Type *PushBack(void) { return IsFull() ? nullptr : &mElements[mLength++]; }
292 
293     /**
294      * This method removes the last element in the array.
295      *
296      * @returns A pointer to the removed element from the array, or `nullptr` if array is empty.
297      *
298      */
PopBack(void)299     Type *PopBack(void) { return IsEmpty() ? nullptr : &mElements[--mLength]; }
300 
301     /**
302      * This method returns the index of an element in the array.
303      *
304      * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
305      *
306      * @param[in] aElement  A reference to an element in the array.
307      *
308      * @returns The index of @p aElement in the array.
309      *
310      */
IndexOf(const Type & aElement) const311     IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - &mElements[0]); }
312 
313     /**
314      * This method removes an element from the array.
315      *
316      * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
317      *
318      * To remove @p aElement, it is replaced by the last element in array, so the order of items in the array can
319      * change after a call to this method.
320      *
321      * The method uses assignment `=` operator on `Type` to copy the last element in place of @p aElement.
322      *
323      */
Remove(Type & aElement)324     void Remove(Type &aElement)
325     {
326         Type *lastElement = PopBack();
327 
328         if (lastElement != &aElement)
329         {
330             aElement = *lastElement;
331         }
332     }
333 
334     /**
335      * This method finds the first match of a given entry in the array.
336      *
337      * This method uses `==` operator on `Type` to compare the array element with @p aEntry.
338      *
339      * @param[in] aEntry   The entry to search for within the array.
340      *
341      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
342      *
343      */
Find(const Type & aEntry)344     Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); }
345 
346     /**
347      * This method finds the first match of a given entry in the array.
348      *
349      * This method uses `==` operator to compare the array elements with @p aEntry.
350      *
351      * @param[in] aEntry   The entry to search for within the array.
352      *
353      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
354      *
355      */
Find(const Type & aEntry) const356     const Type *Find(const Type &aEntry) const
357     {
358         const Type *matched = nullptr;
359 
360         for (const Type &element : *this)
361         {
362             if (element == aEntry)
363             {
364                 matched = &element;
365                 break;
366             }
367         }
368 
369         return matched;
370     }
371 
372     /**
373      * This method indicates whether or not a match to given entry exists in the array.
374      *
375      * This method uses `==` operator on `Type` to compare the array elements with @p aEntry.
376      *
377      * @param[in] aEntry   The entry to search for within the array.
378      *
379      * @retval TRUE   The array contains a matching element with @p aEntry.
380      * @retval FALSE  The array does not contain a matching element with @p aEntry.
381      *
382      */
Contains(const Type & aEntry) const383     bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
384 
385     /**
386      * This template method finds the first element in the array matching a given indicator.
387      *
388      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
389      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
390      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
391      *
392      *     bool Type::Matches(const Indicator &aIndicator) const
393      *
394      * @param[in]  aIndicator  An indicator to match with elements in the array.
395      *
396      * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
397      *
398      */
FindMatching(const Indicator & aIndicator)399     template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
400     {
401         return AsNonConst(AsConst(this)->FindMatching(aIndicator));
402     }
403 
404     /**
405      * This template method finds the first element in the array matching a given indicator.
406      *
407      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
408      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
409      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
410      *
411      *     bool Type::Matches(const Indicator &aIndicator) const
412      *
413      * @param[in]  aIndicator  An indicator to match with elements in the array.
414      *
415      * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
416      *
417      */
FindMatching(const Indicator & aIndicator) const418     template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
419     {
420         const Type *matched = nullptr;
421 
422         for (const Type &element : *this)
423         {
424             if (element.Matches(aIndicator))
425             {
426                 matched = &element;
427                 break;
428             }
429         }
430 
431         return matched;
432     }
433 
434     /**
435      * This template method indicates whether or not the array contains an element matching a given indicator.
436      *
437      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
438      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
439      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
440      *
441      *     bool Type::Matches(const Indicator &aIndicator) const
442      *
443      * @param[in]  aIndicator  An indicator to match with elements in the array.
444      *
445      * @retval TRUE   The array contains a matching element with @p aIndicator.
446      * @retval FALSE  The array does not contain a matching element with @p aIndicator.
447      *
448      */
ContainsMatching(const Indicator & aIndicator) const449     template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
450     {
451         return FindMatching(aIndicator) != nullptr;
452     }
453 
454     /**
455      * This template method removes the first element in the array matching a given indicator.
456      *
457      * This method behaves similar to `Remove()`, i.e., the matched element (if found) is replaced with the last element
458      * in the array (using `=` operator on `Type`). So the order of items in the array can change after a call to this
459      * method.
460      *
461      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
462      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
463      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
464      *
465      *     bool Type::Matches(const Indicator &aIndicator) const
466      *
467      * @param[in]  aIndicator  An indicator to match with elements in the array.
468      *
469      */
RemoveMatching(const Indicator & aIndicator)470     template <typename Indicator> void RemoveMatching(const Indicator &aIndicator)
471     {
472         Type *entry = FindMatching(aIndicator);
473 
474         if (entry != nullptr)
475         {
476             Remove(*entry);
477         }
478     }
479 
480     /**
481      * This template method removes all elements in the array matching a given indicator.
482      *
483      * This method behaves similar to `Remove()`, i.e., a matched element is replaced with the last element in the
484      * array (using `=` operator on `Type`). So the order of items in the array can change after a call to this method.
485      *
486      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
487      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
488      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
489      *
490      *     bool Type::Matches(const Indicator &aIndicator) const
491      *
492      * @param[in]  aIndicator  An indicator to match with elements in the array.
493      *
494      */
RemoveAllMatching(const Indicator & aIndicator)495     template <typename Indicator> void RemoveAllMatching(const Indicator &aIndicator)
496     {
497         for (IndexType index = 0; index < GetLength();)
498         {
499             Type &entry = mElements[index];
500 
501             if (entry.Matches(aIndicator))
502             {
503                 Remove(entry);
504 
505                 // When the entry is removed from the array it is
506                 // replaced with the last element. In this case, we do
507                 // not increment `index`.
508             }
509             else
510             {
511                 index++;
512             }
513         }
514     }
515 
516     /**
517      * This method overloads assignment `=` operator to copy elements from another array into the array.
518      *
519      * The method uses assignment `=` operator on `Type` to copy each element from @p aOtherArray into the elements of
520      * the array.
521      *
522      * @param[in] aOtherArray  Another array to copy from.
523      *
524      */
operator =(const Array & aOtherArray)525     Array &operator=(const Array &aOtherArray)
526     {
527         Clear();
528 
529         for (const Type &otherElement : aOtherArray)
530         {
531             IgnoreError(PushBack(otherElement));
532         }
533 
534         return *this;
535     }
536 
537     // The following methods are intended to support range-based `for`
538     // loop iteration over the array elements and should not be used
539     // directly.
540 
begin(void)541     Type *      begin(void) { return &mElements[0]; }
end(void)542     Type *      end(void) { return &mElements[mLength]; }
begin(void) const543     const Type *begin(void) const { return &mElements[0]; }
end(void) const544     const Type *end(void) const { return &mElements[mLength]; }
545 
546 private:
547     Type      mElements[kMaxSize];
548     IndexType mLength;
549 };
550 
551 } // namespace ot
552 
553 #endif // ARRAY_HPP_
554