diff --git a/include/rcutils/types/string_array.h b/include/rcutils/types/string_array.h index 5a63c3fe..ad16875d 100644 --- a/include/rcutils/types/string_array.h +++ b/include/rcutils/types/string_array.h @@ -110,6 +110,28 @@ RCUTILS_WARN_UNUSED rcutils_ret_t rcutils_string_array_fini(rcutils_string_array_t * string_array); +/// Compare two string arrays. +/** + * The two string arrays are compared according to lexographical order. + * + * \param[in] sa0 The first string array. + * \param[in] sa1 The second string array. + * \param[out] res Negative value if `lhs` appears before `rhs` in lexographical order. + * Zero if `lhs` and `rhs` are equal. + * Positive value if `lhs` appears after `rhs in lexographical order. + * \return `RCUTILS_RET_OK` if successful, or + * \return `RCUTILS_RET_INVALID_ARGUMENT` if any argument is `NULL`, or + * \return `RCUTILS_RET_INVALID_ARGUMENT` if `lhs->data` or `rhs->data` is `NULL`, or + * \return `RCUTILS_RET_ERROR` if an unknown error occurs. + */ +RCUTILS_PUBLIC +RCUTILS_WARN_UNUSED +rcutils_ret_t +rcutils_string_array_cmp( + const rcutils_string_array_t * lhs, + const rcutils_string_array_t * rhs, + int * res); + #ifdef __cplusplus } #endif diff --git a/src/string_array.c b/src/string_array.c index bea53bbd..02ef37d5 100644 --- a/src/string_array.c +++ b/src/string_array.c @@ -18,6 +18,7 @@ extern "C" #endif #include +#include #include "rcutils/allocator.h" #include "rcutils/error_handling.h" @@ -87,6 +88,51 @@ rcutils_string_array_fini(rcutils_string_array_t * string_array) return RCUTILS_RET_OK; } +rcutils_ret_t +rcutils_string_array_cmp( + const rcutils_string_array_t * lhs, + const rcutils_string_array_t * rhs, + int * res) +{ + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + lhs, "lhs string array is null", return RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + rhs, "rhs string array is null", return RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + lhs->data, "lhs->data is null", return RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + rhs->data, "rhs->data is null", return RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + res, "res argument is null", return RCUTILS_RET_INVALID_ARGUMENT); + + size_t smallest_size = lhs->size; + if (rhs->size < smallest_size) { + smallest_size = rhs->size; + } + + for (size_t i = 0; i < smallest_size; ++i) { + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + lhs->data[i], "lhs array element is null", return RCUTILS_RET_ERROR); + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + rhs->data[i], "rhs array element is null", return RCUTILS_RET_ERROR); + // Loop until we find a pair of strings that are not equal + int strcmp_res = strcmp(lhs->data[i], rhs->data[i]); + if (0 != strcmp_res) { + *res = strcmp_res; + return RCUTILS_RET_OK; + } + } + + // If all strings equal, compare array sizes + *res = 0; + if (lhs->size < rhs->size) { + *res = -1; + } else if (lhs->size > rhs->size) { + *res = 1; + } + return RCUTILS_RET_OK; +} + #ifdef __cplusplus } #endif diff --git a/test/test_string_array.cpp b/test/test_string_array.cpp index 937c03f8..8dce9d0c 100644 --- a/test/test_string_array.cpp +++ b/test/test_string_array.cpp @@ -46,3 +46,75 @@ TEST(test_string_array, boot_string_array) { ret = rcutils_string_array_fini(&sa2); ASSERT_EQ(RCUTILS_RET_OK, ret); } + +TEST(test_string_array, string_array_cmp) { + auto allocator = rcutils_get_default_allocator(); + rcutils_ret_t ret = RCUTILS_RET_OK; + int res = 0; + + // Initialize some string arrays + rcutils_string_array_t sa0 = rcutils_get_zero_initialized_string_array(); + ret = rcutils_string_array_init(&sa0, 3, &allocator); + ASSERT_EQ(RCUTILS_RET_OK, ret); + sa0.data[0] = strdup("foo"); + sa0.data[1] = strdup("bar"); + sa0.data[2] = strdup("baz"); + + rcutils_string_array_t sa1 = rcutils_get_zero_initialized_string_array(); + ret = rcutils_string_array_init(&sa1, 3, &allocator); + ASSERT_EQ(RCUTILS_RET_OK, ret); + sa1.data[0] = strdup("foo"); + sa1.data[1] = strdup("bar"); + sa1.data[2] = strdup("baz"); + + rcutils_string_array_t sa2 = rcutils_get_zero_initialized_string_array(); + ret = rcutils_string_array_init(&sa2, 3, &allocator); + ASSERT_EQ(RCUTILS_RET_OK, ret); + sa2.data[0] = strdup("foo"); + sa2.data[1] = strdup("baz"); + sa2.data[2] = strdup("bar"); + + rcutils_string_array_t sa3 = rcutils_get_zero_initialized_string_array(); + ret = rcutils_string_array_init(&sa3, 2, &allocator); + ASSERT_EQ(RCUTILS_RET_OK, ret); + sa3.data[0] = strdup("foo"); + sa3.data[1] = strdup("bar"); + + rcutils_string_array_t incomplete_string_array = rcutils_get_zero_initialized_string_array(); + ret = rcutils_string_array_init(&incomplete_string_array, 3, &allocator); + ASSERT_EQ(RCUTILS_RET_OK, ret); + + // Test failure cases + EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, rcutils_string_array_cmp(NULL, &sa0, &res)); + EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, rcutils_string_array_cmp(&sa0, NULL, &res)); + EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, rcutils_string_array_cmp(&sa0, &sa1, NULL)); + EXPECT_EQ(RCUTILS_RET_ERROR, rcutils_string_array_cmp(&sa0, &incomplete_string_array, &res)); + + // Test success cases + EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa0, &sa1, &res)); + EXPECT_EQ(res, 0); + EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa1, &sa0, &res)); + EXPECT_EQ(res, 0); + EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa0, &sa2, &res)); + EXPECT_LT(res, 0); + EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa2, &sa0, &res)); + EXPECT_GT(res, 0); + EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa0, &sa3, &res)); + EXPECT_GT(res, 0); + EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa3, &sa0, &res)); + EXPECT_LT(res, 0); + // Test transitivity + EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa3, &sa2, &res)); + EXPECT_LT(res, 0); + + ret = rcutils_string_array_fini(&sa0); + ASSERT_EQ(RCUTILS_RET_OK, ret); + ret = rcutils_string_array_fini(&sa1); + ASSERT_EQ(RCUTILS_RET_OK, ret); + ret = rcutils_string_array_fini(&sa2); + ASSERT_EQ(RCUTILS_RET_OK, ret); + ret = rcutils_string_array_fini(&sa3); + ASSERT_EQ(RCUTILS_RET_OK, ret); + ret = rcutils_string_array_fini(&incomplete_string_array); + ASSERT_EQ(RCUTILS_RET_OK, ret); +}