Skip to content

Commit d9bab42

Browse files
jacobperrondirk-thomas
authored andcommitted
Add function rcutils_string_array_cmp (#144)
* Add function rcutils_string_array_cmp Useful for comparing two string arrays according to lexographical order. Signed-off-by: Jacob Perron <jacob@openrobotics.org> * Use RCUTILS_CHECK_FOR_NULL_WITH_MSG Signed-off-by: Jacob Perron <jacob@openrobotics.org> * Add details about invalid arguments Signed-off-by: Jacob Perron <jacob@openrobotics.org>
1 parent d3e2762 commit d9bab42

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

include/rcutils/types/string_array.h

+22
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,28 @@ RCUTILS_WARN_UNUSED
110110
rcutils_ret_t
111111
rcutils_string_array_fini(rcutils_string_array_t * string_array);
112112

113+
/// Compare two string arrays.
114+
/**
115+
* The two string arrays are compared according to lexographical order.
116+
*
117+
* \param[in] sa0 The first string array.
118+
* \param[in] sa1 The second string array.
119+
* \param[out] res Negative value if `lhs` appears before `rhs` in lexographical order.
120+
* Zero if `lhs` and `rhs` are equal.
121+
* Positive value if `lhs` appears after `rhs in lexographical order.
122+
* \return `RCUTILS_RET_OK` if successful, or
123+
* \return `RCUTILS_RET_INVALID_ARGUMENT` if any argument is `NULL`, or
124+
* \return `RCUTILS_RET_INVALID_ARGUMENT` if `lhs->data` or `rhs->data` is `NULL`, or
125+
* \return `RCUTILS_RET_ERROR` if an unknown error occurs.
126+
*/
127+
RCUTILS_PUBLIC
128+
RCUTILS_WARN_UNUSED
129+
rcutils_ret_t
130+
rcutils_string_array_cmp(
131+
const rcutils_string_array_t * lhs,
132+
const rcutils_string_array_t * rhs,
133+
int * res);
134+
113135
#ifdef __cplusplus
114136
}
115137
#endif

src/string_array.c

+46
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern "C"
1818
#endif
1919

2020
#include <stdlib.h>
21+
#include <string.h>
2122

2223
#include "rcutils/allocator.h"
2324
#include "rcutils/error_handling.h"
@@ -87,6 +88,51 @@ rcutils_string_array_fini(rcutils_string_array_t * string_array)
8788
return RCUTILS_RET_OK;
8889
}
8990

91+
rcutils_ret_t
92+
rcutils_string_array_cmp(
93+
const rcutils_string_array_t * lhs,
94+
const rcutils_string_array_t * rhs,
95+
int * res)
96+
{
97+
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
98+
lhs, "lhs string array is null", return RCUTILS_RET_INVALID_ARGUMENT);
99+
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
100+
rhs, "rhs string array is null", return RCUTILS_RET_INVALID_ARGUMENT);
101+
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
102+
lhs->data, "lhs->data is null", return RCUTILS_RET_INVALID_ARGUMENT);
103+
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
104+
rhs->data, "rhs->data is null", return RCUTILS_RET_INVALID_ARGUMENT);
105+
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
106+
res, "res argument is null", return RCUTILS_RET_INVALID_ARGUMENT);
107+
108+
size_t smallest_size = lhs->size;
109+
if (rhs->size < smallest_size) {
110+
smallest_size = rhs->size;
111+
}
112+
113+
for (size_t i = 0; i < smallest_size; ++i) {
114+
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
115+
lhs->data[i], "lhs array element is null", return RCUTILS_RET_ERROR);
116+
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
117+
rhs->data[i], "rhs array element is null", return RCUTILS_RET_ERROR);
118+
// Loop until we find a pair of strings that are not equal
119+
int strcmp_res = strcmp(lhs->data[i], rhs->data[i]);
120+
if (0 != strcmp_res) {
121+
*res = strcmp_res;
122+
return RCUTILS_RET_OK;
123+
}
124+
}
125+
126+
// If all strings equal, compare array sizes
127+
*res = 0;
128+
if (lhs->size < rhs->size) {
129+
*res = -1;
130+
} else if (lhs->size > rhs->size) {
131+
*res = 1;
132+
}
133+
return RCUTILS_RET_OK;
134+
}
135+
90136
#ifdef __cplusplus
91137
}
92138
#endif

test/test_string_array.cpp

+72
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,75 @@ TEST(test_string_array, boot_string_array) {
4646
ret = rcutils_string_array_fini(&sa2);
4747
ASSERT_EQ(RCUTILS_RET_OK, ret);
4848
}
49+
50+
TEST(test_string_array, string_array_cmp) {
51+
auto allocator = rcutils_get_default_allocator();
52+
rcutils_ret_t ret = RCUTILS_RET_OK;
53+
int res = 0;
54+
55+
// Initialize some string arrays
56+
rcutils_string_array_t sa0 = rcutils_get_zero_initialized_string_array();
57+
ret = rcutils_string_array_init(&sa0, 3, &allocator);
58+
ASSERT_EQ(RCUTILS_RET_OK, ret);
59+
sa0.data[0] = strdup("foo");
60+
sa0.data[1] = strdup("bar");
61+
sa0.data[2] = strdup("baz");
62+
63+
rcutils_string_array_t sa1 = rcutils_get_zero_initialized_string_array();
64+
ret = rcutils_string_array_init(&sa1, 3, &allocator);
65+
ASSERT_EQ(RCUTILS_RET_OK, ret);
66+
sa1.data[0] = strdup("foo");
67+
sa1.data[1] = strdup("bar");
68+
sa1.data[2] = strdup("baz");
69+
70+
rcutils_string_array_t sa2 = rcutils_get_zero_initialized_string_array();
71+
ret = rcutils_string_array_init(&sa2, 3, &allocator);
72+
ASSERT_EQ(RCUTILS_RET_OK, ret);
73+
sa2.data[0] = strdup("foo");
74+
sa2.data[1] = strdup("baz");
75+
sa2.data[2] = strdup("bar");
76+
77+
rcutils_string_array_t sa3 = rcutils_get_zero_initialized_string_array();
78+
ret = rcutils_string_array_init(&sa3, 2, &allocator);
79+
ASSERT_EQ(RCUTILS_RET_OK, ret);
80+
sa3.data[0] = strdup("foo");
81+
sa3.data[1] = strdup("bar");
82+
83+
rcutils_string_array_t incomplete_string_array = rcutils_get_zero_initialized_string_array();
84+
ret = rcutils_string_array_init(&incomplete_string_array, 3, &allocator);
85+
ASSERT_EQ(RCUTILS_RET_OK, ret);
86+
87+
// Test failure cases
88+
EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, rcutils_string_array_cmp(NULL, &sa0, &res));
89+
EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, rcutils_string_array_cmp(&sa0, NULL, &res));
90+
EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, rcutils_string_array_cmp(&sa0, &sa1, NULL));
91+
EXPECT_EQ(RCUTILS_RET_ERROR, rcutils_string_array_cmp(&sa0, &incomplete_string_array, &res));
92+
93+
// Test success cases
94+
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa0, &sa1, &res));
95+
EXPECT_EQ(res, 0);
96+
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa1, &sa0, &res));
97+
EXPECT_EQ(res, 0);
98+
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa0, &sa2, &res));
99+
EXPECT_LT(res, 0);
100+
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa2, &sa0, &res));
101+
EXPECT_GT(res, 0);
102+
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa0, &sa3, &res));
103+
EXPECT_GT(res, 0);
104+
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa3, &sa0, &res));
105+
EXPECT_LT(res, 0);
106+
// Test transitivity
107+
EXPECT_EQ(RCUTILS_RET_OK, rcutils_string_array_cmp(&sa3, &sa2, &res));
108+
EXPECT_LT(res, 0);
109+
110+
ret = rcutils_string_array_fini(&sa0);
111+
ASSERT_EQ(RCUTILS_RET_OK, ret);
112+
ret = rcutils_string_array_fini(&sa1);
113+
ASSERT_EQ(RCUTILS_RET_OK, ret);
114+
ret = rcutils_string_array_fini(&sa2);
115+
ASSERT_EQ(RCUTILS_RET_OK, ret);
116+
ret = rcutils_string_array_fini(&sa3);
117+
ASSERT_EQ(RCUTILS_RET_OK, ret);
118+
ret = rcutils_string_array_fini(&incomplete_string_array);
119+
ASSERT_EQ(RCUTILS_RET_OK, ret);
120+
}

0 commit comments

Comments
 (0)