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