1 /* 2 * Copyright (c) 2022, 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 `Heap::Array` (a heap allocated array of flexible length). 32 */ 33 34 #ifndef HEAP_ARRAY_HPP_ 35 #define HEAP_ARRAY_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #include <stdint.h> 40 #include <stdio.h> 41 42 #include "common/array.hpp" 43 #include "common/code_utils.hpp" 44 #include "common/error.hpp" 45 #include "common/heap.hpp" 46 #include "common/new.hpp" 47 48 namespace ot { 49 namespace Heap { 50 51 /** 52 * Represents a heap allocated array. 53 * 54 * The buffer to store the elements is allocated from heap and is managed by the `Heap::Array` class itself. The `Array` 55 * implementation will automatically grow the buffer when new entries are added. The `Heap::Array` destructor will 56 * always free the allocated buffer. 57 * 58 * The `Type` class MUST provide a move constructor `Type(Type &&aOther)` (or a copy constructor if no move constructor 59 * is provided). This constructor is used to move existing elements when array buffer is grown (new buffer is 60 * allocated) to make room for new elements. 61 * 62 * @tparam Type The array element type. 63 * @tparam kCapacityIncrements Number of elements to allocate at a time when updating the array buffer. 64 * 65 */ 66 template <typename Type, uint16_t kCapacityIncrements = 2> class Array 67 { 68 public: 69 using IndexType = uint16_t; 70 71 /** 72 * Initializes the `Array` as empty. 73 * 74 */ Array(void)75 Array(void) 76 : mArray(nullptr) 77 , mLength(0) 78 , mCapacity(0) 79 { 80 } 81 82 /** 83 * This is the destructor for `Array` object. 84 * 85 */ ~Array(void)86 ~Array(void) { Free(); } 87 88 /** 89 * Frees any buffer allocated by the `Array`. 90 * 91 * The `Array` destructor will automatically call `Free()`. This method allows caller to free buffer explicitly. 92 * 93 */ Free(void)94 void Free(void) 95 { 96 Clear(); 97 Heap::Free(mArray); 98 mArray = nullptr; 99 mCapacity = 0; 100 } 101 102 /** 103 * Clears the array. 104 * 105 * Note that `Clear()` method (unlike `Free()`) does not free the allocated buffer and therefore does not change 106 * the current capacity of the array. 107 * 108 * Invokes `Type` destructor on all cleared existing elements of array. 109 * 110 */ Clear(void)111 void Clear(void) 112 { 113 for (Type &entry : *this) 114 { 115 entry.~Type(); 116 } 117 118 mLength = 0; 119 } 120 121 /** 122 * Returns the current array length (number of elements in the array). 123 * 124 * @returns The array length. 125 * 126 */ GetLength(void) const127 IndexType GetLength(void) const { return mLength; } 128 129 /** 130 * Returns a raw pointer to the array buffer. 131 * 132 * The returned raw pointer is valid only while the `Array` remains unchanged. 133 * 134 * @returns A pointer to the array buffer or `nullptr` if the array is empty. 135 * 136 */ AsCArray(void) const137 const Type *AsCArray(void) const { return (mLength != 0) ? mArray : nullptr; } 138 139 /** 140 * Returns the current capacity of array (number of elements that can fit in current allocated buffer). 141 * 142 * The allocated buffer and array capacity are automatically increased (by the `Array` itself) when new elements 143 * are added to array. Removing elements does not change the buffer and the capacity. A desired capacity can be 144 * reserved using `ReserveCapacity()` method. 145 * 146 * @returns The current capacity of the array. 147 * 148 */ GetCapacity(void) const149 IndexType GetCapacity(void) const { return mCapacity; } 150 151 /** 152 * Allocates buffer to reserve a given capacity for array. 153 * 154 * If the requested @p aCapacity is smaller than the current length of the array, capacity remains unchanged. 155 * 156 * @param[in] aCapacity The target capacity for the array. 157 * 158 * @retval kErrorNone Array was successfully updated to support @p aCapacity. 159 * @retval kErrorNoBufs Could not allocate buffer. 160 * 161 */ ReserveCapacity(IndexType aCapacity)162 Error ReserveCapacity(IndexType aCapacity) { return Allocate(aCapacity); } 163 164 /** 165 * Sets the array by taking the buffer from another given array (using move semantics). 166 * 167 * @param[in] aOther The other `Heap::Array` to take from (rvalue reference). 168 * 169 */ TakeFrom(Array && aOther)170 void TakeFrom(Array &&aOther) 171 { 172 Free(); 173 mArray = aOther.mArray; 174 mLength = aOther.mLength; 175 mCapacity = aOther.mCapacity; 176 aOther.mArray = nullptr; 177 aOther.mLength = 0; 178 aOther.mCapacity = 0; 179 } 180 181 /** 182 * Overloads the `[]` operator to get the element at a given index. 183 * 184 * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid. 185 * 186 * @param[in] aIndex The index to get. 187 * 188 * @returns A reference to the element in array at @p aIndex. 189 * 190 */ operator [](IndexType aIndex)191 Type &operator[](IndexType aIndex) { return mArray[aIndex]; } 192 193 /** 194 * Overloads the `[]` operator to get the element at a given index. 195 * 196 * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid. 197 * 198 * @param[in] aIndex The index to get. 199 * 200 * @returns A reference to the element in array at @p aIndex. 201 * 202 */ operator [](IndexType aIndex) const203 const Type &operator[](IndexType aIndex) const { return mArray[aIndex]; } 204 205 /** 206 * Gets a pointer to the element at a given index. 207 * 208 * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned 209 * pointer is valid only while the `Array` remains unchanged. 210 * 211 * @param[in] aIndex The index to get. 212 * 213 * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid. 214 * 215 */ At(IndexType aIndex)216 Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; } 217 218 /** 219 * Gets a pointer to the element at a given index. 220 * 221 * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned 222 * pointer is valid only while the `Array` remains unchanged. 223 * 224 * @param[in] aIndex The index to get. 225 * 226 * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid. 227 * 228 */ At(IndexType aIndex) const229 const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; } 230 231 /** 232 * Gets a pointer to the element at the front of the array (first element). 233 * 234 * The returned pointer is valid only while the `Array` remains unchanged. 235 * 236 * @returns A pointer to the front element or `nullptr` if array is empty. 237 * 238 */ Front(void)239 Type *Front(void) { return At(0); } 240 241 /** 242 * Gets a pointer to the element at the front of the array (first element). 243 * 244 * The returned pointer is valid only while the `Array` remains unchanged. 245 * 246 * @returns A pointer to the front element or `nullptr` if array is empty. 247 * 248 */ Front(void) const249 const Type *Front(void) const { return At(0); } 250 251 /** 252 * Gets a pointer to the element at the back of the array (last element). 253 * 254 * The returned pointer is valid only while the `Array` remains unchanged. 255 * 256 * @returns A pointer to the back element or `nullptr` if array is empty. 257 * 258 */ Back(void)259 Type *Back(void) { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; } 260 261 /** 262 * Gets a pointer to the element at the back of the array (last element). 263 * 264 * The returned pointer is valid only while the `Array` remains unchanged. 265 * 266 * @returns A pointer to the back element or `nullptr` if array is empty. 267 * 268 */ Back(void) const269 const Type *Back(void) const { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; } 270 271 /** 272 * Appends a new entry to the end of the array. 273 * 274 * Requires the `Type` to provide a copy constructor of format `Type(const Type &aOther)` to init the 275 * new element in the array from @p aEntry. 276 * 277 * @param[in] aEntry The new entry to push back. 278 * 279 * @retval kErrorNone Successfully pushed back @p aEntry to the end of the array. 280 * @retval kErrorNoBufs Could not allocate buffer to grow the array. 281 * 282 */ PushBack(const Type & aEntry)283 Error PushBack(const Type &aEntry) 284 { 285 Error error = kErrorNone; 286 287 if (mLength == mCapacity) 288 { 289 SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements)); 290 } 291 292 new (&mArray[mLength++]) Type(aEntry); 293 294 exit: 295 return error; 296 } 297 298 /** 299 * Appends a new entry to the end of the array. 300 * 301 * Requires the `Type` to provide a copy constructor of format `Type(Type &&aOther)` to init the 302 * new element in the array from @p aEntry. 303 * 304 * @param[in] aEntry The new entry to push back (an rvalue reference) 305 * 306 * @retval kErrorNone Successfully pushed back @p aEntry to the end of the array. 307 * @retval kErrorNoBufs Could not allocate buffer to grow the array. 308 * 309 */ PushBack(Type && aEntry)310 Error PushBack(Type &&aEntry) 311 { 312 Error error = kErrorNone; 313 314 if (mLength == mCapacity) 315 { 316 SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements)); 317 } 318 319 new (&mArray[mLength++]) Type(static_cast<Type &&>(aEntry)); 320 321 exit: 322 return error; 323 } 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. This method uses the `Type(void)` default constructor on the newly appended element (if not 330 * `nullptr`). 331 * 332 * The returned pointer is valid only while the `Array` remains unchanged. 333 * 334 * @return A pointer to the newly appended element or `nullptr` if could not allocate buffer to grow the array 335 * 336 */ PushBack(void)337 Type *PushBack(void) 338 { 339 Type *newEntry = nullptr; 340 341 if (mLength == mCapacity) 342 { 343 SuccessOrExit(Allocate(mCapacity + kCapacityIncrements)); 344 } 345 346 newEntry = new (&mArray[mLength++]) Type(); 347 348 exit: 349 return newEntry; 350 } 351 352 /** 353 * Removes the last element in the array. 354 * 355 * Will invoke the `Type` destructor on the removed element. 356 * 357 */ PopBack(void)358 void PopBack(void) 359 { 360 if (mLength > 0) 361 { 362 mArray[mLength - 1].~Type(); 363 mLength--; 364 } 365 } 366 367 /** 368 * Returns the index of an element in the array. 369 * 370 * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined. 371 * 372 * @param[in] aElement A reference to an element in the array. 373 * 374 * @returns The index of @p aElement in the array. 375 * 376 */ IndexOf(const Type & aElement) const377 IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - mArray); } 378 379 /** 380 * Finds the first match of a given entry in the array. 381 * 382 * Uses `==` operator on `Type` to compare the array element with @p aEntry. The returned pointer is 383 * valid only while the `Array` remains unchanged. 384 * 385 * @param[in] aEntry The entry to search for within the array. 386 * 387 * @returns A pointer to matched array element, or `nullptr` if a match could not be found. 388 * 389 */ Find(const Type & aEntry)390 Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); } 391 392 /** 393 * Finds the first match of a given entry in the array. 394 * 395 * Uses `==` operator to compare the array elements with @p aEntry. The returned pointer is valid only 396 * while the `Array` remains unchanged. 397 * 398 * @param[in] aEntry The entry to search for within the array. 399 * 400 * @returns A pointer to matched array element, or `nullptr` if a match could not be found. 401 * 402 */ Find(const Type & aEntry) const403 const Type *Find(const Type &aEntry) const 404 { 405 const Type *matched = nullptr; 406 407 for (const Type &element : *this) 408 { 409 if (element == aEntry) 410 { 411 matched = &element; 412 break; 413 } 414 } 415 416 return matched; 417 } 418 419 /** 420 * Indicates whether or not a match to given entry exists in the array. 421 * 422 * Uses `==` operator on `Type` to compare the array elements with @p aEntry. 423 * 424 * @param[in] aEntry The entry to search for within the array. 425 * 426 * @retval TRUE The array contains a matching element with @p aEntry. 427 * @retval FALSE The array does not contain a matching element with @p aEntry. 428 * 429 */ Contains(const Type & aEntry) const430 bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; } 431 432 /** 433 * Finds the first element in the array matching a given indicator. 434 * 435 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements 436 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each 437 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: 438 * 439 * bool Type::Matches(const Indicator &aIndicator) const 440 * 441 * The returned pointer is valid only while the `Array` remains unchanged. 442 * 443 * @param[in] aIndicator An indicator to match with elements in the array. 444 * 445 * @returns A pointer to the matched array element, or `nullptr` if a match could not be found. 446 * 447 */ FindMatching(const Indicator & aIndicator)448 template <typename Indicator> Type *FindMatching(const Indicator &aIndicator) 449 { 450 return AsNonConst(AsConst(this)->FindMatching(aIndicator)); 451 } 452 453 /** 454 * Finds the first element in the array matching a given indicator. 455 * 456 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements 457 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each 458 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: 459 * 460 * bool Type::Matches(const Indicator &aIndicator) const 461 * 462 * The returned pointer is valid only while the `Array` remains unchanged. 463 * 464 * @param[in] aIndicator An indicator to match with elements in the array. 465 * 466 * @returns A pointer to the matched array element, or `nullptr` if a match could not be found. 467 * 468 */ FindMatching(const Indicator & aIndicator) const469 template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const 470 { 471 const Type *matched = nullptr; 472 473 for (const Type &element : *this) 474 { 475 if (element.Matches(aIndicator)) 476 { 477 matched = &element; 478 break; 479 } 480 } 481 482 return matched; 483 } 484 485 /** 486 * Indicates whether or not the array contains an element matching a given indicator. 487 * 488 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements 489 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each 490 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: 491 * 492 * bool Type::Matches(const Indicator &aIndicator) const 493 * 494 * @param[in] aIndicator An indicator to match with elements in the array. 495 * 496 * @retval TRUE The array contains a matching element with @p aIndicator. 497 * @retval FALSE The array does not contain a matching element with @p aIndicator. 498 * 499 */ ContainsMatching(const Indicator & aIndicator) const500 template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const 501 { 502 return FindMatching(aIndicator) != nullptr; 503 } 504 505 // The following methods are intended to support range-based `for` 506 // loop iteration over the array elements and should not be used 507 // directly. 508 begin(void)509 Type *begin(void) { return (mLength > 0) ? mArray : nullptr; } end(void)510 Type *end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; } begin(void) const511 const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; } end(void) const512 const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; } 513 514 Array(const Array &) = delete; 515 Array &operator=(const Array &) = delete; 516 517 private: Allocate(IndexType aCapacity)518 Error Allocate(IndexType aCapacity) 519 { 520 Error error = kErrorNone; 521 Type *newArray; 522 523 VerifyOrExit((aCapacity != mCapacity) && (aCapacity >= mLength)); 524 newArray = static_cast<Type *>(Heap::CAlloc(aCapacity, sizeof(Type))); 525 VerifyOrExit(newArray != nullptr, error = kErrorNoBufs); 526 527 for (IndexType index = 0; index < mLength; index++) 528 { 529 new (&newArray[index]) Type(static_cast<Type &&>(mArray[index])); 530 mArray[index].~Type(); 531 } 532 533 Heap::Free(mArray); 534 mArray = newArray; 535 mCapacity = aCapacity; 536 537 exit: 538 return error; 539 } 540 541 Type *mArray; 542 IndexType mLength; 543 IndexType mCapacity; 544 }; 545 546 } // namespace Heap 547 } // namespace ot 548 549 #endif // HEAP_ARRAY_HPP_ 550