[array] add Array::InsertAt() method (#13522)

This commit adds the `InsertAt(aIndex)` method to the `Array` template
class to support inserting a new element at a specific index.

Specifically:
- Shifts existing elements to make room for the new entry when
  `aIndex <= mLength` and the array is not full.
- Returns a pointer to the newly inserted element for caller
  initialization, or `nullptr` if the array is full or `aIndex` is
  invalid.
- Adds unit test for the `InsertAt()` in `test_array.cpp`.
This commit is contained in:
Abtin Keshavarzian
2026-08-19 17:52:14 -07:00
committed by GitHub
parent 54ac4cb987
commit 054df099ab
2 changed files with 208 additions and 0 deletions
+67
View File
@@ -349,6 +349,55 @@ public:
*/
Type *PopBack(void) OT_LIFETIME_BOUND { return IsEmpty() ? nullptr : &mElements[--mLength]; }
/**
* Inserts a new entry at a given index in the array.
*
* The method uses assignment `=` operator on `Type` to copy @p aEntry into the inserted array element. Existing
* elements at and after @p aIndex are shifted right.
*
* The @p aEntry can be from the array itself. The method safely copies @p aEntry before shifting existing
* elements in the array.
*
* @param[in] aIndex The index at which to insert the new element.
* @param[in] aEntry The new entry to insert.
*
* @retval kErrorNone Successfully inserted @p aEntry at @p aIndex in the array.
* @retval kErrorNoBufs Could not insert the new element since array is full.
* @retval kErrorInvalidArgs @p aIndex is invalid (`aIndex > GetLength()`).
*/
Error InsertAt(IndexType aIndex, const Type &aEntry)
{
Error error;
Type entry = aEntry;
SuccessOrExit(error = ShiftFrom(aIndex));
mElements[aIndex] = entry;
exit:
return error;
}
/**
* Inserts a new element at a given index in the array.
*
* On success, this method shifts existing elements to make room and returns a pointer to the inserted element
* in the array for the caller to initialize and use.
*
* @param[in] aIndex The index at which to insert the new element.
*
* @returns A pointer to the newly inserted element, or `nullptr` if the array is full or @p aIndex is invalid.
*/
Type *InsertAt(IndexType aIndex) OT_LIFETIME_BOUND
{
Type *entry = nullptr;
SuccessOrExit(ShiftFrom(aIndex));
entry = &mElements[aIndex];
exit:
return entry;
}
/**
* Returns the index of an element in the array.
*
@@ -628,6 +677,24 @@ public:
const Type *end(void) const OT_LIFETIME_BOUND { return &mElements[mLength]; }
private:
Error ShiftFrom(IndexType aIndex)
{
Error error = kErrorNone;
VerifyOrExit(!IsFull(), error = kErrorNoBufs);
VerifyOrExit(aIndex <= mLength, error = kErrorInvalidArgs);
for (IndexType index = mLength; index > aIndex; index--)
{
mElements[index] = mElements[index - 1];
}
mLength++;
exit:
return error;
}
Type mElements[kMaxSize];
IndexType mLength;
};
+141
View File
@@ -407,6 +407,146 @@ void TestArrayCopyAndFindMatching(void)
printf("\n");
}
void TestArrayInsertAt(void)
{
constexpr uint16_t kMaxSize = 6;
Array<uint16_t, kMaxSize> array;
uint16_t *entry;
// Verify invalid insert on empty array (out-of-bounds index)
VerifyOrQuit(array.InsertAt(1) == nullptr);
VerifyOrQuit(array.IsEmpty());
// Insert at index 0 in empty array (equivalent to `PushBack`)
entry = array.InsertAt(0);
VerifyOrQuit(entry != nullptr);
*entry = 20;
VerifyOrQuit(array.GetLength() == 1);
VerifyOrQuit(array[0] == 20);
// Insert at index 0 (at the beginning): array becomes [10, 20]
entry = array.InsertAt(0);
VerifyOrQuit(entry != nullptr);
*entry = 10;
VerifyOrQuit(array.GetLength() == 2);
VerifyOrQuit(array[0] == 10);
VerifyOrQuit(array[1] == 20);
// Verify invalid insert on partially filled array (index > length)
VerifyOrQuit(array.InsertAt(3) == nullptr);
VerifyOrQuit(array.InsertAt(kMaxSize) == nullptr);
VerifyOrQuit(array.GetLength() == 2);
// Insert at index 2 (at the end): array becomes [10, 20, 40]
entry = array.InsertAt(2);
VerifyOrQuit(entry != nullptr);
*entry = 40;
VerifyOrQuit(array.GetLength() == 3);
VerifyOrQuit(array[0] == 10);
VerifyOrQuit(array[1] == 20);
VerifyOrQuit(array[2] == 40);
// Insert at index 2 (in the middle): array becomes [10, 20, 30, 40]
entry = array.InsertAt(2);
VerifyOrQuit(entry != nullptr);
*entry = 30;
VerifyOrQuit(array.GetLength() == 4);
VerifyOrQuit(array[0] == 10);
VerifyOrQuit(array[1] == 20);
VerifyOrQuit(array[2] == 30);
VerifyOrQuit(array[3] == 40);
// Insert at index 1 (in the middle): array becomes [10, 15, 20, 30, 40]
entry = array.InsertAt(1);
VerifyOrQuit(entry != nullptr);
*entry = 15;
VerifyOrQuit(array.GetLength() == 5);
VerifyOrQuit(array[0] == 10);
VerifyOrQuit(array[1] == 15);
VerifyOrQuit(array[2] == 20);
VerifyOrQuit(array[3] == 30);
VerifyOrQuit(array[4] == 40);
// Insert at index 5 (at the end): array becomes [10, 15, 20, 30, 40, 50]
entry = array.InsertAt(5);
VerifyOrQuit(entry != nullptr);
*entry = 50;
VerifyOrQuit(array.GetLength() == 6);
VerifyOrQuit(array.IsFull());
VerifyOrQuit(array[0] == 10);
VerifyOrQuit(array[1] == 15);
VerifyOrQuit(array[2] == 20);
VerifyOrQuit(array[3] == 30);
VerifyOrQuit(array[4] == 40);
VerifyOrQuit(array[5] == 50);
// Verify insertion fails when array is full
VerifyOrQuit(array.InsertAt(0) == nullptr);
VerifyOrQuit(array.InsertAt(3) == nullptr);
VerifyOrQuit(array.InsertAt(6) == nullptr);
VerifyOrQuit(array.InsertAt(7) == nullptr);
VerifyOrQuit(array.GetLength() == 6);
// Test `Error InsertAt(IndexType, const Type &)` overload
array.Clear();
VerifyOrQuit(array.InsertAt(1, 100) == kErrorInvalidArgs);
SuccessOrQuit(array.InsertAt(0, 200));
SuccessOrQuit(array.InsertAt(0, 100));
SuccessOrQuit(array.InsertAt(2, 400));
SuccessOrQuit(array.InsertAt(2, 300));
VerifyOrQuit(array.GetLength() == 4);
VerifyOrQuit(array[0] == 100);
VerifyOrQuit(array[1] == 200);
VerifyOrQuit(array[2] == 300);
VerifyOrQuit(array[3] == 400);
VerifyOrQuit(array.InsertAt(5, 500) == kErrorInvalidArgs);
SuccessOrQuit(array.InsertAt(4, 500));
SuccessOrQuit(array.InsertAt(5, 600));
VerifyOrQuit(array.IsFull());
VerifyOrQuit(array.InsertAt(0, 50) == kErrorNoBufs);
VerifyOrQuit(array.InsertAt(3, 250) == kErrorNoBufs);
VerifyOrQuit(array.InsertAt(6, 700) == kErrorNoBufs);
VerifyOrQuit(array.InsertAt(7, 800) == kErrorNoBufs);
// Test inserting an element that is from the array itself,
// ensuring it is safely copied before existing elements are shifted.
array.Clear();
SuccessOrQuit(array.PushBack(100));
SuccessOrQuit(array.PushBack(200));
SuccessOrQuit(array.PushBack(300));
// Insert `array[1]` (200) at index 0: `array[1]` gets shifted right during insertion
SuccessOrQuit(array.InsertAt(0, array[1]));
VerifyOrQuit(array.GetLength() == 4);
VerifyOrQuit(array[0] == 200);
VerifyOrQuit(array[1] == 100);
VerifyOrQuit(array[2] == 200);
VerifyOrQuit(array[3] == 300);
// Insert `array[3]` (300) at index 1: `array[3]` gets shifted right during insertion
SuccessOrQuit(array.InsertAt(1, array[3]));
VerifyOrQuit(array.GetLength() == 5);
VerifyOrQuit(array[0] == 200);
VerifyOrQuit(array[1] == 300);
VerifyOrQuit(array[2] == 100);
VerifyOrQuit(array[3] == 200);
VerifyOrQuit(array[4] == 300);
// Insert `array[0]` (200) at index 0: self-insertion at the exact index
SuccessOrQuit(array.InsertAt(0, array[0]));
VerifyOrQuit(array.GetLength() == 6);
VerifyOrQuit(array[0] == 200);
VerifyOrQuit(array[1] == 200);
VerifyOrQuit(array[2] == 300);
VerifyOrQuit(array[3] == 100);
VerifyOrQuit(array[4] == 200);
VerifyOrQuit(array[5] == 300);
printf("TestArrayInsertAt PASSED\n\n");
}
void TestArrayIndexType(void)
{
typedef Array<uint16_t, 255> Array1;
@@ -424,6 +564,7 @@ int main(void)
{
ot::TestArray();
ot::TestArrayCopyAndFindMatching();
ot::TestArrayInsertAt();
ot::TestArrayIndexType();
printf("All tests passed\n");