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