Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ set(rcutils_sources
src/hash_map.c
src/logging.c
src/process.c
src/qsort.c
src/repl_str.c
src/shared_library.c
src/snprintf.c
Expand Down
51 changes: 51 additions & 0 deletions include/rcutils/qsort.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCUTILS__QSORT_H_
#define RCUTILS__QSORT_H_

#ifdef __cplusplus
extern "C"
{
#endif

#include "rcutils/macros.h"
#include "rcutils/types/rcutils_ret.h"
#include "rcutils/visibility_control.h"

/// Interface to qsort with rcutils-style argument validation.
/**
* This function changes the order of the elements in the array so that they
* are in ascending order according to the given comparison function.
*
* This function is thread-safe.
*
* \param[inout] ptr object whose elements should be sorted.
* \param[in] count number of elements present in the object.
* \param[in] size size of each element, in bytes.
* \param[in] comp function used to compare two elements.
* \return `RCUTILS_RET_OK` if successful, or
* \return `RCUTILS_RET_INVALID_ARGUMENT` for invalid arguments, or
* \return `RCUTILS_RET_ERROR` if an unknown error occurs.
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_qsort(void * ptr, size_t count, size_t size, int (* comp)(const void *, const void *));

#ifdef __cplusplus
}
#endif

#endif // RCUTILS__QSORT_H_
48 changes: 46 additions & 2 deletions include/rcutils/types/string_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ extern "C"
#include <string.h>

#include "rcutils/allocator.h"
#include "rcutils/error_handling.h"
#include "rcutils/macros.h"
#include "rcutils/qsort.h"
#include "rcutils/types/rcutils_ret.h"
#include "rcutils/visibility_control.h"

Expand Down Expand Up @@ -113,11 +115,11 @@ rcutils_string_array_fini(rcutils_string_array_t * string_array);

/// Compare two string arrays.
/**
* The two string arrays are compared according to lexographical order.
* The two string arrays are compared according to lexicographical order.
*
* \param[in] sa0 The first string array.
* \param[in] sa1 The second string array.
Comment thread
cottsay marked this conversation as resolved.
Outdated
* \param[out] res Negative value if `lhs` appears before `rhs` in lexographical order.
* \param[out] res Negative value if `lhs` appears before `rhs` in lexicographical 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
Expand Down Expand Up @@ -162,6 +164,48 @@ rcutils_string_array_resize(
rcutils_string_array_t * string_array,
size_t new_size);

/// Lexicographic comparer for pointers to string pointers.
/**
* This functions compares pointers to string pointers lexicographically
* ascending.
*
* \param[in] lhs pointer to the first string pointer.
* \param[in] rhs pointer to the second string pointer.
* \return <0 if lhs is lexicographically lower, or
* \return 0 if the strings are the same, or
* \return >0 if lhs is lexicographically higher.
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
int
rcutils_string_array_sort_compare(const void * lhs, const void * rhs);

/// Sort a string array according to lexicographical order.
/**
* This function changes the order of the entries in a string array so that
* they are in lexicographically ascending order. Empty entries are placed at the
Comment thread
cottsay marked this conversation as resolved.
Outdated
* end of the array.
*
* \param[inout] string_array object whose elements should be sorted.
* \return `RCUTILS_RET_OK` if successful, or
* \return `RCUTILS_RET_INVALID_ARGUMENT` for invalid arguments, or
* \return `RCUTILS_RET_ERROR` if an unknown error occurs.
*/
inline
RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_string_array_sort(rcutils_string_array_t * string_array)
{
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
string_array, "string_array is null", return RCUTILS_RET_INVALID_ARGUMENT);

return rcutils_qsort(
string_array->data,
string_array->size,
sizeof(string_array->data[0]),
rcutils_string_array_sort_compare);
}

#ifdef __cplusplus
}
#endif
Expand Down
45 changes: 45 additions & 0 deletions src/qsort.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdlib.h>

#include "rcutils/error_handling.h"
#include "rcutils/qsort.h"

rcutils_ret_t
rcutils_qsort(void * ptr, size_t count, size_t size, int (* comp)(const void *, const void *))
{
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
comp, "comp is null", return RCUTILS_RET_INVALID_ARGUMENT);

if (1 >= count) {
return RCUTILS_RET_OK;
}

RCUTILS_CHECK_FOR_NULL_WITH_MSG(
ptr, "ptr is null", return RCUTILS_RET_INVALID_ARGUMENT);

qsort(ptr, count, size, comp);

return RCUTILS_RET_OK;
}

#ifdef __cplusplus
}
#endif
13 changes: 13 additions & 0 deletions src/string_array.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,19 @@ rcutils_string_array_resize(
return rcutils_string_array_fini(&to_reclaim);
}

int
rcutils_string_array_sort_compare(const void * lhs, const void * rhs)
{
const char * left = *(const char **)lhs;
const char * right = *(const char **)rhs;
if (NULL == left) {
return NULL == right ? 0 : 1;
} else if (NULL == right) {
return -1;
}
return strcmp(left, right);
}

#ifdef __cplusplus
}
#endif
72 changes: 72 additions & 0 deletions test/test_string_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,75 @@ TEST(test_string_array, string_array_resize) {
ret = rcutils_string_array_fini(&sa0);
ASSERT_EQ(RCUTILS_RET_OK, ret);
}

TEST(test_string_array, string_array_sort) {
auto allocator = rcutils_get_default_allocator();
rcutils_ret_t ret = RCUTILS_RET_OK;

ret = rcutils_string_array_sort(nullptr);
Comment thread
cottsay marked this conversation as resolved.
EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, ret);
rcutils_reset_error();

rcutils_string_array_t sa0 = rcutils_get_zero_initialized_string_array();
ret = rcutils_string_array_sort(&sa0);
EXPECT_EQ(RCUTILS_RET_OK, ret);

ret = rcutils_string_array_init(&sa0, 8, &allocator);
ASSERT_EQ(RCUTILS_RET_OK, ret);

// Already in order
for (size_t i = 0; i < sa0.size; i++) {
const char val[] = {static_cast<char>('a' + i), '\0'};
sa0.data[i] = strdup(val);
Comment thread
cottsay marked this conversation as resolved.
}

ret = rcutils_string_array_sort(&sa0);
EXPECT_EQ(RCUTILS_RET_OK, ret);
for (size_t i = 0; i < sa0.size; i++) {
const char val[] = {static_cast<char>('a' + i), '\0'};
EXPECT_STREQ(val, sa0.data[i]);
}

// Reverse order
for (size_t i = 0; i < sa0.size; i++) {
const char val[] = {static_cast<char>('a' + sa0.size - 1 - i), '\0'};
sa0.allocator.deallocate(sa0.data[i], sa0.allocator.state);
sa0.data[i] = strdup(val);
Comment thread
cottsay marked this conversation as resolved.
}

ret = rcutils_string_array_sort(&sa0);
EXPECT_EQ(RCUTILS_RET_OK, ret);
for (size_t i = 0; i < sa0.size; i++) {
const char val[] = {static_cast<char>('a' + i), '\0'};
EXPECT_STREQ(val, sa0.data[i]);
}

// Make some entries empty
for (size_t i = 0; i < sa0.size / 2; i++) {
sa0.allocator.deallocate(sa0.data[i], sa0.allocator.state);
sa0.data[i] = nullptr;
}

ret = rcutils_string_array_sort(&sa0);
EXPECT_EQ(RCUTILS_RET_OK, ret);
for (size_t i = 0; i < sa0.size / 2; i++) {
const char val[] = {static_cast<char>('a' + i + (sa0.size / 2)), '\0'};
EXPECT_STREQ(val, sa0.data[i]);
}
for (size_t i = sa0.size / 2; i < sa0.size; i++) {
EXPECT_STREQ(nullptr, sa0.data[i]);
}

// Already in order, with empty entries
ret = rcutils_string_array_sort(&sa0);
EXPECT_EQ(RCUTILS_RET_OK, ret);
for (size_t i = 0; i < sa0.size / 2; i++) {
const char val[] = {static_cast<char>('a' + i + (sa0.size / 2)), '\0'};
EXPECT_STREQ(val, sa0.data[i]);
}
for (size_t i = sa0.size / 2; i < sa0.size; i++) {
EXPECT_STREQ(nullptr, sa0.data[i]);
}

ASSERT_EQ(RCUTILS_RET_OK, rcutils_string_array_fini(&sa0));
}