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_sort function #248

Merged
merged 9 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all 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_
52 changes: 48 additions & 4 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.
* \param[out] res Negative value if `lhs` appears before `rhs` in lexographical order.
* \param[in] lhs The first string array.
* \param[in] rhs The second string array.
* \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 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);
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);
cottsay marked this conversation as resolved.
Show resolved Hide 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);
cottsay marked this conversation as resolved.
Show resolved Hide 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));
}