Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rcutils_string_array_resize function #247

Merged
merged 19 commits into from
Jun 8, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions include/rcutils/types/string_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,27 @@ rcutils_string_array_cmp(
const rcutils_string_array_t * rhs,
int * res);

/// Resize a string array, reclaiming removed resources.
/**
* This function changes the size of an existing string array. If the new size
* is larger, new entries are added to the end of the array and are zero-
* initialized. If the new size is smaller, entries are removed from the end of
* the array and their resources reclaimed.
*
* \param[inout] string_array object to be resized.
* \param[in] new_size the size the array should be changed to.
* \return `RCUTILS_RET_OK` if successful, or
* \return `RCUTILS_RET_INVALID_ARGUMENT` for invalid arguments, or
* \return `RCUTILS_RET_BAD_ALLOC` if memory allocation fails, or
* \return `RCUTILS_RET_ERROR` if an unknown error occurs.
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_string_array_resize(
rcutils_string_array_t * string_array,
size_t new_size);

#ifdef __cplusplus
}
#endif
Expand Down
46 changes: 46 additions & 0 deletions src/string_array.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ rcutils_string_array_fini(rcutils_string_array_t * string_array)
}
allocator->deallocate(string_array->data, allocator->state);
string_array->data = NULL;
string_array->size = 0;

return RCUTILS_RET_OK;
}
Expand Down Expand Up @@ -133,6 +134,51 @@ rcutils_string_array_cmp(
return RCUTILS_RET_OK;
}

rcutils_ret_t
rcutils_string_array_resize(
rcutils_string_array_t * string_array,
size_t new_size)
{
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
string_array, "string_array is null", return RCUTILS_RET_INVALID_ARGUMENT);

if (string_array->size == new_size) {
return RCUTILS_RET_OK;
}

if (0 == new_size) {
return rcutils_string_array_fini(string_array);
}

// Reclaim entries being removed
rcutils_allocator_t * allocator = &string_array->allocator;
if (!rcutils_allocator_is_valid(allocator)) {
RCUTILS_SET_ERROR_MSG("allocator is invalid");
return RCUTILS_RET_INVALID_ARGUMENT;
}
for (size_t i = new_size; i < string_array->size; ++i) {
allocator->deallocate(string_array->data[i], allocator->state);
string_array->data[i] = NULL;
}

char ** new_data = allocator->reallocate(
string_array->data, new_size * sizeof(char *), allocator->state);
if (NULL == new_data) {
RCUTILS_SET_ERROR_MSG("failed to allocator string array");
return RCUTILS_RET_BAD_ALLOC;
}
string_array->data = new_data;

// Zero-initialize new entries
for (size_t i = string_array->size; i < new_size; ++i) {
string_array->data[i] = NULL;
}

string_array->size = new_size;

return RCUTILS_RET_OK;
}

#ifdef __cplusplus
}
#endif
70 changes: 70 additions & 0 deletions test/test_string_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,73 @@ TEST(test_string_array, string_array_cmp) {
ret = rcutils_string_array_fini(&incomplete_string_array);
ASSERT_EQ(RCUTILS_RET_OK, ret);
}

TEST(test_string_array, string_array_resize) {
auto allocator = rcutils_get_default_allocator();
auto failing_allocator = get_failing_allocator();
auto invalid_allocator = rcutils_get_zero_initialized_allocator();
rcutils_ret_t ret;

ret = rcutils_string_array_resize(nullptr, 8);
ASSERT_EQ(RCUTILS_RET_INVALID_ARGUMENT, ret);

// Start with 8 elements
rcutils_string_array_t sa0;
ret = rcutils_string_array_init(&sa0, 8, &allocator);
ASSERT_EQ(RCUTILS_RET_OK, ret);

for (size_t i = 0; i < sa0.size; i++) {
const char val[] = {static_cast<char>('a' + i), '\0'};
sa0.data[i] = strdup(val);
}

// Resize to same size (hot path)
ret = rcutils_string_array_resize(&sa0, sa0.size);
ASSERT_EQ(RCUTILS_RET_OK, ret);

// Grow to 16
ret = rcutils_string_array_resize(&sa0, 16);
ASSERT_EQ(RCUTILS_RET_OK, ret);
ASSERT_EQ(16u, sa0.size);

// Check that existing data is intact
for (size_t i = 0; i < 8; i++) {
const char val[] = {static_cast<char>('a' + i), '\0'};
EXPECT_STREQ(val, sa0.data[i]);
}

// Check that new elements are empty
for (size_t i = 8; i < sa0.size; i++) {
const char val[] = {static_cast<char>('a' + i), '\0'};
EXPECT_STREQ(nullptr, sa0.data[i]);
sa0.data[i] = strdup(val);
}

// Shrink to 4
ret = rcutils_string_array_resize(&sa0, 4);
ASSERT_EQ(RCUTILS_RET_OK, ret);
ASSERT_EQ(4u, sa0.size);

// Check that existing data is intact
for (size_t i = 0; i < sa0.size; i++) {
const char val[] = {static_cast<char>('a' + i), '\0'};
EXPECT_STREQ(val, sa0.data[i]);
}

// Shrink to 0
ret = rcutils_string_array_resize(&sa0, 0);
EXPECT_EQ(RCUTILS_RET_OK, ret);
EXPECT_EQ(0u, sa0.size);

// Allocation failure
sa0.allocator = failing_allocator;
ret = rcutils_string_array_resize(&sa0, 8);
EXPECT_EQ(RCUTILS_RET_BAD_ALLOC, ret);
EXPECT_EQ(0u, sa0.size);

// Invalid allocator
sa0.allocator = invalid_allocator;
ret = rcutils_string_array_resize(&sa0, 8);
EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, ret);
EXPECT_EQ(0u, sa0.size);
}