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/error.hpp"
41 #include "common/numeric_limits.hpp"
42 #include "common/type_traits.hpp"
43 
44 namespace ot {
45 
46 /**
47  * This template class represents an array of elements with a fixed max size.
48  *
49  * @tparam Type        The array element type.
50  * @tparam kMaxSize    Specifies the max array size (maximum number of elements in the array).
51  * @tparam SizeType    The type to be used for array size, length, and index. If not specified, a default `uint` type
52  *                     is determined based on `kMaxSize`, i.e., if `kMaxSize <= 255` then `uint8_t` will be used,
53  *                     otherwise `uint16_t` will be used.
54  *
55  */
56 template <typename Type,
57           uint16_t kMaxSize,
58           typename SizeType =
59               typename TypeTraits::Conditional<kMaxSize <= NumericLimits<uint8_t>::kMax, uint8_t, uint16_t>::Type>
60 class Array
61 {
62     static_assert(kMaxSize != 0, "Array `kMaxSize` cannot be zero");
63 
64 public:
65     /**
66      * This type represents the length or index in array.
67      *
68      * It is typically either `uint8_t` or `uint16_t` (determined based on the maximum array size (`kMaxSize`)).
69      *
70      */
71     typedef SizeType IndexType;
72 
73     /**
74      * This constructor initializes the array as empty.
75      *
76      */
Array(void)77     Array(void)
78         : mLength(0)
79     {
80     }
81 
82     /**
83      * This constructor initializes the array by copying elements from another array.
84      *
85      * The method uses assignment `=` operator on `Type` to copy each element from @p aOtherArray into the elements of
86      * the array.
87      *
88      * @param[in] aOtherArray  Another array to copy from.
89      *
90      */
Array(const Array & aOtherArray)91     Array(const Array &aOtherArray) { *this = aOtherArray; }
92 
93     /**
94      * This method clears the array.
95      *
96      */
Clear(void)97     void Clear(void) { mLength = 0; }
98 
99     /**
100      * This method indicates whether or not the array is empty.
101      *
102      * @retval TRUE when array is empty.
103      * @retval FALSE when array is not empty.
104      *
105      */
IsEmpty(void) const106     bool IsEmpty(void) const { return (mLength == 0); }
107 
108     /**
109      * This method indicates whether or not the array is full.
110      *
111      * @retval TRUE when array is full.
112      * @retval FALSE when array is not full.
113      *
114      */
IsFull(void) const115     bool IsFull(void) const { return (mLength == GetMaxSize()); }
116 
117     /**
118      * This method returns the maximum array size (max number of elements).
119      *
120      * @returns The maximum array size (max number of elements that can be added to the array).
121      *
122      */
GetMaxSize(void) const123     IndexType GetMaxSize(void) const { return static_cast<IndexType>(kMaxSize); }
124 
125     /**
126      * This method returns the current length of array (number of elements).
127      *
128      * @returns The current array length.
129      *
130      */
GetLength(void) const131     IndexType GetLength(void) const { return mLength; }
132 
133     /**
134      * This method overloads the `[]` operator to get the element at a given index.
135      *
136      * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
137      *
138      * @param[in] aIndex  The index to get.
139      *
140      * @returns A reference to the element in array at @p aIndex.
141      *
142      */
operator [](IndexType aIndex)143     Type &operator[](IndexType aIndex) { return mElements[aIndex]; }
144 
145     /**
146      * This method overloads the `[]` operator to get the element at a given index.
147      *
148      * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
149      *
150      * @param[in] aIndex  The index to get.
151      *
152      * @returns A reference to the element in array at @p aIndex.
153      *
154      */
operator [](IndexType aIndex) const155     const Type &operator[](IndexType aIndex) const { return mElements[aIndex]; }
156 
157     /**
158      * This method gets a pointer to the element at a given index.
159      *
160      * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length.
161      *
162      * @param[in] aIndex  The index to get.
163      *
164      * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
165      *
166      */
At(IndexType aIndex)167     Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mElements[aIndex] : nullptr; }
168 
169     /**
170      * This method gets a pointer to the element at a given index.
171      *
172      * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length.
173      *
174      * @param[in] aIndex  The index to get.
175      *
176      * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
177      *
178      */
At(IndexType aIndex) const179     const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mElements[aIndex] : nullptr; }
180 
181     /**
182      * This method gets a pointer to the element at the front of the array (first element).
183      *
184      * @returns A pointer to the front element or `nullptr` if array is empty.
185      *
186      */
Front(void)187     Type *Front(void) { return At(0); }
188 
189     /**
190      * This method gets a pointer to the element at the front of the array (first element).
191      *
192      * @returns A pointer to the front element or `nullptr` if array is empty.
193      *
194      */
Front(void) const195     const Type *Front(void) const { return At(0); }
196 
197     /**
198      * This method gets a pointer to the element at the back of the array (last element).
199      *
200      * @returns A pointer to the back element or `nullptr` if array is empty.
201      *
202      */
Back(void)203     Type *Back(void) { return At(mLength - 1); }
204 
205     /**
206      * This method gets a pointer to the element at the back of the array (last element).
207      *
208      * @returns A pointer to the back element or `nullptr` if array is empty.
209      *
210      */
Back(void) const211     const Type *Back(void) const { return At(mLength - 1); }
212 
213     /**
214      * This method appends a new entry to the end of the array.
215      *
216      * The method uses assignment `=` operator on `Type` to copy @p aEntry into the added array element.
217      *
218      * @param[in] aEntry     The new entry to push back.
219      *
220      * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
221      * @retval kErrorNoBufs  Could not append the new element since array is full.
222      *
223      */
PushBack(const Type & aEntry)224     Error PushBack(const Type &aEntry) { return IsFull() ? kErrorNoBufs : (mElements[mLength++] = aEntry, kErrorNone); }
225 
226     /**
227      * This method appends a new entry to the end of the array.
228      *
229      * On success, this method returns a pointer to the newly appended element in the array for the caller to
230      * initialize and use.
231      *
232      * @return A pointer to the newly appended element or `nullptr` if array is full.
233      *
234      */
PushBack(void)235     Type *PushBack(void) { return IsFull() ? nullptr : &mElements[mLength++]; }
236 
237     /**
238      * This method removes the last element in the array.
239      *
240      * @returns A pointer to the removed element from the array, or `nullptr` if array is empty.
241      *
242      */
PopBack(void)243     Type *PopBack(void) { return IsEmpty() ? nullptr : &mElements[--mLength]; }
244 
245     /**
246      * This method returns the index of an element in the array.
247      *
248      * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
249      *
250      * @param[in] aElement  A reference to an element in the array.
251      *
252      * @returns The index of @p aElement in the array.
253      *
254      */
IndexOf(const Type & aElement) const255     IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - &mElements[0]); }
256 
257     /**
258      * This method finds the first match of a given entry in the array.
259      *
260      * This method uses `==` operator on `Type` to compare the array element with @p aEntry.
261      *
262      * @param[in] aEntry   The entry to search for within the array.
263      *
264      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
265      *
266      */
Find(const Type & aEntry)267     Type *Find(const Type &aEntry) { return const_cast<Type *>(const_cast<const Array *>(this)->Find(aEntry)); }
268 
269     /**
270      * This method finds the first match of a given entry in the array.
271      *
272      * This method uses `==` operator to compare the array elements with @p aEntry.
273      *
274      * @param[in] aEntry   The entry to search for within the array.
275      *
276      * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
277      *
278      */
Find(const Type & aEntry) const279     const Type *Find(const Type &aEntry) const
280     {
281         const Type *matched = nullptr;
282 
283         for (const Type &element : *this)
284         {
285             if (element == aEntry)
286             {
287                 matched = &element;
288                 break;
289             }
290         }
291 
292         return matched;
293     }
294 
295     /**
296      * This method indicates whether or not a match to given entry exists in the array.
297      *
298      * This method uses `==` operator on `Type` to compare the array elements with @p aEntry.
299      *
300      * @param[in] aEntry   The entry to search for within the array.
301      *
302      * @retval TRUE   The array contains a matching element with @p aEntry.
303      * @retval FALSE  The array does not contain a matching element with @p aEntry.
304      *
305      */
Contains(const Type & aEntry) const306     bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
307 
308     /**
309      * This template method finds the first element in the array matching a given indicator.
310      *
311      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
312      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
313      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
314      *
315      *     bool Type::Matches(const Indicator &aIndicator) const
316      *
317      * @param[in]  aIndicator  An indicator to match with elements in the array.
318      *
319      * @returns A pointer to the matched array element, or nullptr if a match could not be found.
320      *
321      */
FindMatching(const Indicator & aIndicator)322     template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
323     {
324         return const_cast<Type *>(const_cast<const Array *>(this)->FindMatching(aIndicator));
325     }
326 
327     /**
328      * This template method finds the first element in the array matching a given indicator.
329      *
330      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
331      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
332      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
333      *
334      *     bool Type::Matches(const Indicator &aIndicator) const
335      *
336      * @param[in]  aIndicator  An indicator to match with elements in the array.
337      *
338      * @returns A pointer to the matched array element, or nullptr if a match could not be found.
339      *
340      */
FindMatching(const Indicator & aIndicator) const341     template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
342     {
343         const Type *matched = nullptr;
344 
345         for (const Type &element : *this)
346         {
347             if (element.Matches(aIndicator))
348             {
349                 matched = &element;
350                 break;
351             }
352         }
353 
354         return matched;
355     }
356 
357     /**
358      * This template method indicates whether or not the array contains an element matching a given indicator.
359      *
360      * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
361      * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
362      * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
363      *
364      *     bool Type::Matches(const Indicator &aIndicator) const
365      *
366      * @param[in]  aIndicator  An indicator to match with elements in the array.
367      *
368      * @retval TRUE   The array contains a matching element with @p aIndicator.
369      * @retval FALSE  The array does not contain a matching element with @p aIndicator.
370      *
371      */
ContainsMatching(const Indicator & aIndicator) const372     template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
373     {
374         return FindMatching(aIndicator) != nullptr;
375     }
376 
377     /**
378      * This method overloads assignment `=` operator to copy elements from another array into the array.
379      *
380      * The method uses assignment `=` operator on `Type` to copy each element from @p aOtherArray into the elements of
381      * the array.
382      *
383      * @param[in] aOtherArray  Another array to copy from.
384      *
385      */
operator =(const Array & aOtherArray)386     Array &operator=(const Array &aOtherArray)
387     {
388         Clear();
389 
390         for (const Type &otherElement : aOtherArray)
391         {
392             IgnoreError(PushBack(otherElement));
393         }
394 
395         return *this;
396     }
397 
398     // The following methods are intended to support range-based `for`
399     // loop iteration over the array elements and should not be used
400     // directly.
401 
begin(void)402     Type *      begin(void) { return &mElements[0]; }
end(void)403     Type *      end(void) { return &mElements[mLength]; }
begin(void) const404     const Type *begin(void) const { return &mElements[0]; }
end(void) const405     const Type *end(void) const { return &mElements[mLength]; }
406 
407 private:
408     Type      mElements[kMaxSize];
409     IndexType mLength;
410 };
411 
412 } // namespace ot
413 
414 #endif // ARRAY_HPP_
415