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 template <typename Type, uint16_t kCapacityIncrements = 2> class Array 66 { 67 public: 68 using IndexType = uint16_t; 69 70 /** 71 * Initializes the `Array` as empty. 72 */ Array(void)73 Array(void) 74 : mArray(nullptr) 75 , mLength(0) 76 , mCapacity(0) 77 { 78 } 79 80 /** 81 * This is the destructor for `Array` object. 82 */ ~Array(void)83 ~Array(void) { Free(); } 84 85 /** 86 * Frees any buffer allocated by the `Array`. 87 * 88 * The `Array` destructor will automatically call `Free()`. This method allows caller to free buffer explicitly. 89 */ Free(void)90 void Free(void) 91 { 92 Clear(); 93 Heap::Free(mArray); 94 mArray = nullptr; 95 mCapacity = 0; 96 } 97 98 /** 99 * Clears the array. 100 * 101 * Note that `Clear()` method (unlike `Free()`) does not free the allocated buffer and therefore does not change 102 * the current capacity of the array. 103 * 104 * Invokes `Type` destructor on all cleared existing elements of array. 105 */ Clear(void)106 void Clear(void) 107 { 108 for (Type &entry : *this) 109 { 110 entry.~Type(); 111 } 112 113 mLength = 0; 114 } 115 116 /** 117 * Returns the current array length (number of elements in the array). 118 * 119 * @returns The array length. 120 */ GetLength(void) const121 IndexType GetLength(void) const { return mLength; } 122 123 /** 124 * Returns a raw pointer to the array buffer. 125 * 126 * The returned raw pointer is valid only while the `Array` remains unchanged. 127 * 128 * @returns A pointer to the array buffer or `nullptr` if the array is empty. 129 */ AsCArray(void) const130 const Type *AsCArray(void) const { return (mLength != 0) ? mArray : nullptr; } 131 132 /** 133 * Returns the current capacity of array (number of elements that can fit in current allocated buffer). 134 * 135 * The allocated buffer and array capacity are automatically increased (by the `Array` itself) when new elements 136 * are added to array. Removing elements does not change the buffer and the capacity. A desired capacity can be 137 * reserved using `ReserveCapacity()` method. 138 * 139 * @returns The current capacity of the array. 140 */ GetCapacity(void) const141 IndexType GetCapacity(void) const { return mCapacity; } 142 143 /** 144 * Allocates buffer to reserve a given capacity for array. 145 * 146 * If the requested @p aCapacity is smaller than the current length of the array, capacity remains unchanged. 147 * 148 * @param[in] aCapacity The target capacity for the array. 149 * 150 * @retval kErrorNone Array was successfully updated to support @p aCapacity. 151 * @retval kErrorNoBufs Could not allocate buffer. 152 */ ReserveCapacity(IndexType aCapacity)153 Error ReserveCapacity(IndexType aCapacity) { return Allocate(aCapacity); } 154 155 /** 156 * Sets the array by taking the buffer from another given array (using move semantics). 157 * 158 * @param[in] aOther The other `Heap::Array` to take from (rvalue reference). 159 */ TakeFrom(Array && aOther)160 void TakeFrom(Array &&aOther) 161 { 162 Free(); 163 mArray = aOther.mArray; 164 mLength = aOther.mLength; 165 mCapacity = aOther.mCapacity; 166 aOther.mArray = nullptr; 167 aOther.mLength = 0; 168 aOther.mCapacity = 0; 169 } 170 171 /** 172 * Overloads the `[]` operator to get the element at a given index. 173 * 174 * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid. 175 * 176 * @param[in] aIndex The index to get. 177 * 178 * @returns A reference to the element in array at @p aIndex. 179 */ operator [](IndexType aIndex)180 Type &operator[](IndexType aIndex) { return mArray[aIndex]; } 181 182 /** 183 * Overloads the `[]` operator to get the element at a given index. 184 * 185 * Does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid. 186 * 187 * @param[in] aIndex The index to get. 188 * 189 * @returns A reference to the element in array at @p aIndex. 190 */ operator [](IndexType aIndex) const191 const Type &operator[](IndexType aIndex) const { return mArray[aIndex]; } 192 193 /** 194 * Gets a pointer to the element at a given index. 195 * 196 * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned 197 * pointer is valid only while the `Array` remains unchanged. 198 * 199 * @param[in] aIndex The index to get. 200 * 201 * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid. 202 */ At(IndexType aIndex)203 Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; } 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 */ At(IndexType aIndex) const215 const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; } 216 217 /** 218 * Gets a pointer to the element at the front of the array (first element). 219 * 220 * The returned pointer is valid only while the `Array` remains unchanged. 221 * 222 * @returns A pointer to the front element or `nullptr` if array is empty. 223 */ Front(void)224 Type *Front(void) { return At(0); } 225 226 /** 227 * Gets a pointer to the element at the front of the array (first element). 228 * 229 * The returned pointer is valid only while the `Array` remains unchanged. 230 * 231 * @returns A pointer to the front element or `nullptr` if array is empty. 232 */ Front(void) const233 const Type *Front(void) const { return At(0); } 234 235 /** 236 * Gets a pointer to the element at the back of the array (last element). 237 * 238 * The returned pointer is valid only while the `Array` remains unchanged. 239 * 240 * @returns A pointer to the back element or `nullptr` if array is empty. 241 */ Back(void)242 Type *Back(void) { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; } 243 244 /** 245 * Gets a pointer to the element at the back of the array (last element). 246 * 247 * The returned pointer is valid only while the `Array` remains unchanged. 248 * 249 * @returns A pointer to the back element or `nullptr` if array is empty. 250 */ Back(void) const251 const Type *Back(void) const { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; } 252 253 /** 254 * Appends a new entry to the end of the array. 255 * 256 * Requires the `Type` to provide a copy constructor of format `Type(const Type &aOther)` to init the 257 * new element in the array from @p aEntry. 258 * 259 * @param[in] aEntry The new entry to push back. 260 * 261 * @retval kErrorNone Successfully pushed back @p aEntry to the end of the array. 262 * @retval kErrorNoBufs Could not allocate buffer to grow the array. 263 */ PushBack(const Type & aEntry)264 Error PushBack(const Type &aEntry) 265 { 266 Error error = kErrorNone; 267 268 if (mLength == mCapacity) 269 { 270 SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements)); 271 } 272 273 new (&mArray[mLength++]) Type(aEntry); 274 275 exit: 276 return error; 277 } 278 279 /** 280 * Appends a new entry to the end of the array. 281 * 282 * Requires the `Type` to provide a copy constructor of format `Type(Type &&aOther)` to init the 283 * new element in the array from @p aEntry. 284 * 285 * @param[in] aEntry The new entry to push back (an rvalue reference) 286 * 287 * @retval kErrorNone Successfully pushed back @p aEntry to the end of the array. 288 * @retval kErrorNoBufs Could not allocate buffer to grow the array. 289 */ PushBack(Type && aEntry)290 Error PushBack(Type &&aEntry) 291 { 292 Error error = kErrorNone; 293 294 if (mLength == mCapacity) 295 { 296 SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements)); 297 } 298 299 new (&mArray[mLength++]) Type(static_cast<Type &&>(aEntry)); 300 301 exit: 302 return error; 303 } 304 305 /** 306 * Appends a new entry to the end of the array. 307 * 308 * On success, this method returns a pointer to the newly appended element in the array for the caller to 309 * initialize and use. This method uses the `Type(void)` default constructor on the newly appended element (if not 310 * `nullptr`). 311 * 312 * The returned pointer is valid only while the `Array` remains unchanged. 313 * 314 * @return A pointer to the newly appended element or `nullptr` if could not allocate buffer to grow the array 315 */ PushBack(void)316 Type *PushBack(void) 317 { 318 Type *newEntry = nullptr; 319 320 if (mLength == mCapacity) 321 { 322 SuccessOrExit(Allocate(mCapacity + kCapacityIncrements)); 323 } 324 325 newEntry = new (&mArray[mLength++]) Type(); 326 327 exit: 328 return newEntry; 329 } 330 331 /** 332 * Removes the last element in the array. 333 * 334 * Will invoke the `Type` destructor on the removed element. 335 */ PopBack(void)336 void PopBack(void) 337 { 338 if (mLength > 0) 339 { 340 mArray[mLength - 1].~Type(); 341 mLength--; 342 } 343 } 344 345 /** 346 * Returns the index of an element in the array. 347 * 348 * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined. 349 * 350 * @param[in] aElement A reference to an element in the array. 351 * 352 * @returns The index of @p aElement in the array. 353 */ IndexOf(const Type & aElement) const354 IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - mArray); } 355 356 /** 357 * Finds the first match of a given entry in the array. 358 * 359 * Uses `==` operator on `Type` to compare the array element with @p aEntry. The returned pointer is 360 * valid only while the `Array` remains unchanged. 361 * 362 * @param[in] aEntry The entry to search for within the array. 363 * 364 * @returns A pointer to matched array element, or `nullptr` if a match could not be found. 365 */ Find(const Type & aEntry)366 Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); } 367 368 /** 369 * Finds the first match of a given entry in the array. 370 * 371 * Uses `==` operator to compare the array elements with @p aEntry. The returned pointer is valid only 372 * while the `Array` remains unchanged. 373 * 374 * @param[in] aEntry The entry to search for within the array. 375 * 376 * @returns A pointer to matched array element, or `nullptr` if a match could not be found. 377 */ Find(const Type & aEntry) const378 const Type *Find(const Type &aEntry) const 379 { 380 const Type *matched = nullptr; 381 382 for (const Type &element : *this) 383 { 384 if (element == aEntry) 385 { 386 matched = &element; 387 break; 388 } 389 } 390 391 return matched; 392 } 393 394 /** 395 * Indicates whether or not a match to given entry exists in the array. 396 * 397 * Uses `==` operator on `Type` to compare the array elements with @p aEntry. 398 * 399 * @param[in] aEntry The entry to search for within the array. 400 * 401 * @retval TRUE The array contains a matching element with @p aEntry. 402 * @retval FALSE The array does not contain a matching element with @p aEntry. 403 */ Contains(const Type & aEntry) const404 bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; } 405 406 /** 407 * Finds the first element in the array matching a given indicator. 408 * 409 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements 410 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each 411 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: 412 * 413 * bool Type::Matches(const Indicator &aIndicator) const 414 * 415 * The returned pointer is valid only while the `Array` remains unchanged. 416 * 417 * @param[in] aIndicator An indicator to match with elements in the array. 418 * 419 * @returns A pointer to the matched array element, or `nullptr` if a match could not be found. 420 */ FindMatching(const Indicator & aIndicator)421 template <typename Indicator> Type *FindMatching(const Indicator &aIndicator) 422 { 423 return AsNonConst(AsConst(this)->FindMatching(aIndicator)); 424 } 425 426 /** 427 * Finds the first element in the array matching a given indicator. 428 * 429 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements 430 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each 431 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: 432 * 433 * bool Type::Matches(const Indicator &aIndicator) const 434 * 435 * The returned pointer is valid only while the `Array` remains unchanged. 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 */ FindMatching(const Indicator & aIndicator) const441 template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const 442 { 443 const Type *matched = nullptr; 444 445 for (const Type &element : *this) 446 { 447 if (element.Matches(aIndicator)) 448 { 449 matched = &element; 450 break; 451 } 452 } 453 454 return matched; 455 } 456 457 /** 458 * Indicates whether or not the array contains an element matching a given indicator. 459 * 460 * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements 461 * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each 462 * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: 463 * 464 * bool Type::Matches(const Indicator &aIndicator) const 465 * 466 * @param[in] aIndicator An indicator to match with elements in the array. 467 * 468 * @retval TRUE The array contains a matching element with @p aIndicator. 469 * @retval FALSE The array does not contain a matching element with @p aIndicator. 470 */ ContainsMatching(const Indicator & aIndicator) const471 template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const 472 { 473 return FindMatching(aIndicator) != nullptr; 474 } 475 476 // The following methods are intended to support range-based `for` 477 // loop iteration over the array elements and should not be used 478 // directly. 479 begin(void)480 Type *begin(void) { return (mLength > 0) ? mArray : nullptr; } end(void)481 Type *end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; } begin(void) const482 const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; } end(void) const483 const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; } 484 485 Array(const Array &) = delete; 486 Array &operator=(const Array &) = delete; 487 488 private: Allocate(IndexType aCapacity)489 Error Allocate(IndexType aCapacity) 490 { 491 Error error = kErrorNone; 492 Type *newArray; 493 494 VerifyOrExit((aCapacity != mCapacity) && (aCapacity >= mLength)); 495 newArray = static_cast<Type *>(Heap::CAlloc(aCapacity, sizeof(Type))); 496 VerifyOrExit(newArray != nullptr, error = kErrorNoBufs); 497 498 for (IndexType index = 0; index < mLength; index++) 499 { 500 new (&newArray[index]) Type(static_cast<Type &&>(mArray[index])); 501 mArray[index].~Type(); 502 } 503 504 Heap::Free(mArray); 505 mArray = newArray; 506 mCapacity = aCapacity; 507 508 exit: 509 return error; 510 } 511 512 Type *mArray; 513 IndexType mLength; 514 IndexType mCapacity; 515 }; 516 517 } // namespace Heap 518 } // namespace ot 519 520 #endif // HEAP_ARRAY_HPP_ 521