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/locator.hpp"
43 #include "common/numeric_limits.hpp"
44 #include "common/type_traits.hpp"
45
46 namespace ot {
47
48 /**
49 * Returns the length of a given array (number of elements in the array).
50 *
51 * This template function is `constexpr`. The template arguments are expected to be deduced by the compiler allowing
52 * callers to simply use `GetArrayLength(aArray)`.
53 *
54 * @tparam Type The array element type.
55 * @tparam kArrayLength The array length.
56 *
57 * @returns The array length (number of elements in the array).
58 *
59 */
GetArrayLength(const Type (&)[kArrayLength])60 template <typename Type, uint16_t kArrayLength> constexpr inline uint16_t GetArrayLength(const Type (&)[kArrayLength])
61 {
62 return kArrayLength;
63 }
64
65 /**
66 * Returns a pointer to end of a given array (pointing to the past-the-end element).
67 *
68 * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
69 * not point to an actual element in array, and thus should not be dereferenced.
70 *
71 * @tparam Type The array element type.
72 * @tparam kArrayLength The array length.
73 *
74 * @param[in] aArray A reference to the array.
75 *
76 * @returns Pointer to the past-the-end element.
77 *
78 */
GetArrayEnd(Type (& aArray)[kArrayLength])79 template <typename Type, uint16_t kArrayLength> inline Type *GetArrayEnd(Type (&aArray)[kArrayLength])
80 {
81 return &aArray[kArrayLength];
82 }
83
84 /**
85 * Returns a pointer to end of a given array (pointing to the past-the-end element).
86 *
87 * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
88 * not point to an actual element in array, and thus should not be dereferenced.
89 *
90 * @tparam Type The array element type.
91 * @tparam kArrayLength The array length.
92 *
93 * @param[in] aArray A reference to the array.
94 *
95 * @returns Pointer to the past-the-end element.
96 *
97 */
GetArrayEnd(const Type (& aArray)[kArrayLength])98 template <typename Type, uint16_t kArrayLength> inline const Type *GetArrayEnd(const Type (&aArray)[kArrayLength])
99 {
100 return &aArray[kArrayLength];
101 }
102
103 /**
104 * Represents an array of elements with a fixed max size.
105 *
106 * @tparam Type The array element type.
107 * @tparam kMaxSize Specifies the max array size (maximum number of elements in the array).
108 * @tparam SizeType The type to be used for array size, length, and index. If not specified, a default `uint` type
109 * is determined based on `kMaxSize`, i.e., if `kMaxSize <= 255` then `uint8_t` will be used,
110 * otherwise `uint16_t` will be used.
111 *
112 */
113 template <typename Type,
114 uint16_t kMaxSize,
115 typename SizeType =
116 typename TypeTraits::Conditional<kMaxSize <= NumericLimits<uint8_t>::kMax, uint8_t, uint16_t>::Type>
117 class Array
118 {
119 static_assert(kMaxSize != 0, "Array `kMaxSize` cannot be zero");
120
121 public:
122 /**
123 * Represents the length or index in array.
124 *
125 * It is typically either `uint8_t` or `uint16_t` (determined based on the maximum array size (`kMaxSize`)).
126 *
127 */
128 typedef SizeType IndexType;
129
130 /**
131 * Initializes the array as empty.
132 *
133 */
Array(void)134 Array(void)
135 : mLength(0)
136 {
137 }
138
139 /**
140 * Initializes the array by copying elements from another array.
141 *
142 * The method uses assignment `=` operator on `Type` to copy each element from @p aOtherArray into the elements of
143 * the array.
144 *
145 * @param[in] aOtherArray Another array to copy from.
146 *
147 */
Array(const Array & aOtherArray)148 Array(const Array &aOtherArray) { *this = aOtherArray; }
149
150 /**
151 * Initializes the array as empty and initializes its elements by calling `Init(Instance &)`
152 * method on every element.
153 *
154 * Uses method `Init(Instance &aInstance)` on `Type`.
155 *
156 * @param[in] aInstance The OpenThread instance.
157 *
158 */
Array(Instance & aInstance)159 explicit Array(Instance &aInstance)
160 : mLength(0)
161 {
162 for (Type &element : mElements)
163 {
164 element.Init(aInstance);
165 }
166 }
167
168 /**
169 * Clears the array.
170 *
171 */
Clear(void)172 void Clear(void) { mLength = 0; }
173
174 /**
175 * Indicates whether or not the array is empty.
176 *
177 * @retval TRUE when array is empty.
178 * @retval FALSE when array is not empty.
179 *
180 */
IsEmpty(void) const181 bool IsEmpty(void) const { return (mLength == 0); }
182
183 /**
184 * Indicates whether or not the array is full.
185 *
186 * @retval TRUE when array is full.
187 * @retval FALSE when array is not full.
188 *
189 */
IsFull(void) const190 bool IsFull(void) const { return (mLength == GetMaxSize()); }
191
192 /**
193 * Returns the maximum array size (max number of elements).
194 *
195 * @returns The maximum array size (max number of elements that can be added to the array).
196 *
197 */
GetMaxSize(void) const198 IndexType GetMaxSize(void) const { return static_cast<IndexType>(kMaxSize); }
199
200 /**
201 * Returns the current length of array (number of elements).
202 *
203 * @returns The current array length.
204 *
205 */
GetLength(void) const206 IndexType GetLength(void) const { return mLength; }
207
208 /**
209 * Sets the current length (number of elements) of the array.
210 *
211 * @param[in] aLength The array length.
212 *
213 */
SetLength(IndexType aLength)214 void SetLength(IndexType aLength) { mLength = aLength; }
215
216 /**
217 * Returns the pointer to the start of underlying C array buffer serving as `Array` storage.
218 *
219 * @return The pointer to start of underlying C array buffer.
220 *
221 */
GetArrayBuffer(void)222 Type *GetArrayBuffer(void) { return mElements; }
223
224 /**
225 * Returns the pointer to the start of underlying C array buffer serving as `Array` storage.
226 *
227 * @return The pointer to start of underlying C array buffer.
228 *
229 */
GetArrayBuffer(void) const230 const Type *GetArrayBuffer(void) const { return mElements; }
231
232 /**
233 * Overloads the `[]` operator to get the element at a given index.
234 *
235 * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
236 *
237 * @param[in] aIndex The index to get.
238 *
239 * @returns A reference to the element in array at @p aIndex.
240 *
241 */
operator [](IndexType aIndex)242 Type &operator[](IndexType aIndex) { return mElements[aIndex]; }
243
244 /**
245 * Overloads the `[]` operator to get the element at a given index.
246 *
247 * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
248 *
249 * @param[in] aIndex The index to get.
250 *
251 * @returns A reference to the element in array at @p aIndex.
252 *
253 */
operator [](IndexType aIndex) const254 const Type &operator[](IndexType aIndex) const { return mElements[aIndex]; }
255
256 /**
257 * Gets a pointer to the element at a given index.
258 *
259 * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length.
260 *
261 * @param[in] aIndex The index to get.
262 *
263 * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
264 *
265 */
At(IndexType aIndex)266 Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mElements[aIndex] : nullptr; }
267
268 /**
269 * Gets a pointer to the element at a given index.
270 *
271 * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length.
272 *
273 * @param[in] aIndex The index to get.
274 *
275 * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
276 *
277 */
At(IndexType aIndex) const278 const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mElements[aIndex] : nullptr; }
279
280 /**
281 * Gets a pointer to the element at the front of the array (first element).
282 *
283 * @returns A pointer to the front element or `nullptr` if array is empty.
284 *
285 */
Front(void)286 Type *Front(void) { return At(0); }
287
288 /**
289 * Gets a pointer to the element at the front of the array (first element).
290 *
291 * @returns A pointer to the front element or `nullptr` if array is empty.
292 *
293 */
Front(void) const294 const Type *Front(void) const { return At(0); }
295
296 /**
297 * Gets a pointer to the element at the back of the array (last element).
298 *
299 * @returns A pointer to the back element or `nullptr` if array is empty.
300 *
301 */
Back(void)302 Type *Back(void) { return At(mLength - 1); }
303
304 /**
305 * Gets a pointer to the element at the back of the array (last element).
306 *
307 * @returns A pointer to the back element or `nullptr` if array is empty.
308 *
309 */
Back(void) const310 const Type *Back(void) const { return At(mLength - 1); }
311
312 /**
313 * Appends a new entry to the end of the array.
314 *
315 * The method uses assignment `=` operator on `Type` to copy @p aEntry into the added array element.
316 *
317 * @param[in] aEntry The new entry to push back.
318 *
319 * @retval kErrorNone Successfully pushed back @p aEntry to the end of the array.
320 * @retval kErrorNoBufs Could not append the new element since array is full.
321 *
322 */
PushBack(const Type & aEntry)323 Error PushBack(const Type &aEntry) { return IsFull() ? kErrorNoBufs : (mElements[mLength++] = aEntry, kErrorNone); }
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.
330 *
331 * @return A pointer to the newly appended element or `nullptr` if array is full.
332 *
333 */
PushBack(void)334 Type *PushBack(void) { return IsFull() ? nullptr : &mElements[mLength++]; }
335
336 /**
337 * Removes the last element in the array.
338 *
339 * @returns A pointer to the removed element from the array, or `nullptr` if array is empty.
340 *
341 */
PopBack(void)342 Type *PopBack(void) { return IsEmpty() ? nullptr : &mElements[--mLength]; }
343
344 /**
345 * Returns the index of an element in the array.
346 *
347 * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
348 *
349 * @param[in] aElement A reference to an element in the array.
350 *
351 * @returns The index of @p aElement in the array.
352 *
353 */
IndexOf(const Type & aElement) const354 IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - &mElements[0]); }
355
356 /**
357 * Removes an element from the array.
358 *
359 * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
360 *
361 * To remove @p aElement, it is replaced by the last element in array, so the order of items in the array can
362 * change after a call to this method.
363 *
364 * The method uses assignment `=` operator on `Type` to copy the last element in place of @p aElement.
365 *
366 */
Remove(Type & aElement)367 void Remove(Type &aElement)
368 {
369 Type *lastElement = PopBack();
370
371 if (lastElement != &aElement)
372 {
373 aElement = *lastElement;
374 }
375 }
376
377 /**
378 * Finds the first match of a given entry in the array.
379 *
380 * Uses `==` operator on `Type` to compare the array element with @p aEntry.
381 *
382 * @param[in] aEntry The entry to search for within the array.
383 *
384 * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
385 *
386 */
Find(const Type & aEntry)387 Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); }
388
389 /**
390 * Finds the first match of a given entry in the array.
391 *
392 * Uses `==` operator to compare the array elements with @p aEntry.
393 *
394 * @param[in] aEntry The entry to search for within the array.
395 *
396 * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
397 *
398 */
Find(const Type & aEntry) const399 const Type *Find(const Type &aEntry) const
400 {
401 const Type *matched = nullptr;
402
403 for (const Type &element : *this)
404 {
405 if (element == aEntry)
406 {
407 matched = &element;
408 break;
409 }
410 }
411
412 return matched;
413 }
414
415 /**
416 * Indicates whether or not a match to given entry exists in the array.
417 *
418 * Uses `==` operator on `Type` to compare the array elements with @p aEntry.
419 *
420 * @param[in] aEntry The entry to search for within the array.
421 *
422 * @retval TRUE The array contains a matching element with @p aEntry.
423 * @retval FALSE The array does not contain a matching element with @p aEntry.
424 *
425 */
Contains(const Type & aEntry) const426 bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
427
428 /**
429 * Finds the first element in the array matching a given indicator.
430 *
431 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
432 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
433 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
434 *
435 * bool Type::Matches(const Indicator &aIndicator) const
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 *
441 */
FindMatching(const Indicator & aIndicator)442 template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
443 {
444 return AsNonConst(AsConst(this)->FindMatching(aIndicator));
445 }
446
447 /**
448 * Finds the first element in the array matching a given indicator.
449 *
450 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
451 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
452 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
453 *
454 * bool Type::Matches(const Indicator &aIndicator) const
455 *
456 * @param[in] aIndicator An indicator to match with elements in the array.
457 *
458 * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
459 *
460 */
FindMatching(const Indicator & aIndicator) const461 template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
462 {
463 const Type *matched = nullptr;
464
465 for (const Type &element : *this)
466 {
467 if (element.Matches(aIndicator))
468 {
469 matched = &element;
470 break;
471 }
472 }
473
474 return matched;
475 }
476
477 /**
478 * Indicates whether or not the array contains an element matching a given indicator.
479 *
480 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
481 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
482 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
483 *
484 * bool Type::Matches(const Indicator &aIndicator) const
485 *
486 * @param[in] aIndicator An indicator to match with elements in the array.
487 *
488 * @retval TRUE The array contains a matching element with @p aIndicator.
489 * @retval FALSE The array does not contain a matching element with @p aIndicator.
490 *
491 */
ContainsMatching(const Indicator & aIndicator) const492 template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
493 {
494 return FindMatching(aIndicator) != nullptr;
495 }
496
497 /**
498 * Removes the first element in the array matching a given indicator.
499 *
500 * Behaves similar to `Remove()`, i.e., the matched element (if found) is replaced with the last element
501 * in the array (using `=` operator on `Type`). So the order of items in the array can change after a call to this
502 * method.
503 *
504 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
505 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
506 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
507 *
508 * bool Type::Matches(const Indicator &aIndicator) const
509 *
510 * @param[in] aIndicator An indicator to match with elements in the array.
511 *
512 */
RemoveMatching(const Indicator & aIndicator)513 template <typename Indicator> void RemoveMatching(const Indicator &aIndicator)
514 {
515 Type *entry = FindMatching(aIndicator);
516
517 if (entry != nullptr)
518 {
519 Remove(*entry);
520 }
521 }
522
523 /**
524 * Removes all elements in the array matching a given indicator.
525 *
526 * Behaves similar to `Remove()`, i.e., a matched element is replaced with the last element in the
527 * array (using `=` operator on `Type`). So the order of items in the array can change after a call to this method.
528 *
529 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
530 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
531 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
532 *
533 * bool Type::Matches(const Indicator &aIndicator) const
534 *
535 * @param[in] aIndicator An indicator to match with elements in the array.
536 *
537 */
RemoveAllMatching(const Indicator & aIndicator)538 template <typename Indicator> void RemoveAllMatching(const Indicator &aIndicator)
539 {
540 for (IndexType index = 0; index < GetLength();)
541 {
542 Type &entry = mElements[index];
543
544 if (entry.Matches(aIndicator))
545 {
546 Remove(entry);
547
548 // When the entry is removed from the array it is
549 // replaced with the last element. In this case, we do
550 // not increment `index`.
551 }
552 else
553 {
554 index++;
555 }
556 }
557 }
558
559 /**
560 * Overloads assignment `=` operator to copy elements from another array into the array.
561 *
562 * The method uses assignment `=` operator on `Type` to copy each element from @p aOtherArray into the elements of
563 * the array.
564 *
565 * @param[in] aOtherArray Another array to copy from.
566 *
567 */
operator =(const Array & aOtherArray)568 Array &operator=(const Array &aOtherArray)
569 {
570 Clear();
571
572 for (const Type &otherElement : aOtherArray)
573 {
574 IgnoreError(PushBack(otherElement));
575 }
576
577 return *this;
578 }
579
580 /**
581 * Indicates whether a given entry pointer is from the array buffer.
582 *
583 * Does not check the current length of array and only checks that @p aEntry is pointing to an address
584 * contained within underlying C array buffer.
585 *
586 * @param[in] aEntry A pointer to an entry to check.
587 *
588 * @retval TRUE The @p aEntry is from the array.
589 * @retval FALSE The @p aEntry is not from the array.
590 *
591 */
IsInArrayBuffer(const Type * aEntry) const592 bool IsInArrayBuffer(const Type *aEntry) const
593 {
594 return (&mElements[0] <= aEntry) && (aEntry < GetArrayEnd(mElements));
595 }
596
597 // The following methods are intended to support range-based `for`
598 // loop iteration over the array elements and should not be used
599 // directly.
600
begin(void)601 Type *begin(void) { return &mElements[0]; }
end(void)602 Type *end(void) { return &mElements[mLength]; }
begin(void) const603 const Type *begin(void) const { return &mElements[0]; }
end(void) const604 const Type *end(void) const { return &mElements[mLength]; }
605
606 private:
607 Type mElements[kMaxSize];
608 IndexType mLength;
609 };
610
611 } // namespace ot
612
613 #endif // ARRAY_HPP_
614