# HG changeset patch # User Mike Becker # Date 1766056290 -3600 # Node ID bf5d647f939dbeea4d28c353e303dbdac5c8669b # Parent 0db02ab1457ccd040d393697d2dc770bfb94ede7 use qsort_r() when it is available - relates to #622 diff -r 0db02ab1457c -r bf5d647f939d configure --- a/configure Wed Dec 17 20:13:08 2025 +0100 +++ b/configure Thu Dec 18 12:11:30 2025 +0100 @@ -156,9 +156,10 @@ --enable-api-docs run Doxygen during build --enable-coverage test coverage with gcov --enable-asan address sanitizer - --disable-memrchr --disable-cxx-tests the check-cxx makefile target --disable-szmul-builtin use custom implementation, instead + --disable-memrchr + --disable-qsort_r __EOF__ abort_configure @@ -278,9 +279,10 @@ fi # features -FEATURE_MEMRCHR=auto FEATURE_CXX_TESTS=auto FEATURE_SZMUL_BUILTIN=auto +FEATURE_MEMRCHR=auto +FEATURE_QSORT_R=auto # # parse arguments @@ -304,12 +306,14 @@ "--disable-coverage") unset FEATURE_COVERAGE ;; "--enable-asan") FEATURE_ASAN=on ;; "--disable-asan") unset FEATURE_ASAN ;; - "--enable-memrchr") FEATURE_MEMRCHR=on ;; - "--disable-memrchr") unset FEATURE_MEMRCHR ;; "--enable-cxx-tests") FEATURE_CXX_TESTS=on ;; "--disable-cxx-tests") unset FEATURE_CXX_TESTS ;; "--enable-szmul-builtin") FEATURE_SZMUL_BUILTIN=on ;; "--disable-szmul-builtin") unset FEATURE_SZMUL_BUILTIN ;; + "--enable-memrchr") FEATURE_MEMRCHR=on ;; + "--disable-memrchr") unset FEATURE_MEMRCHR ;; + "--enable-qsort_r") FEATURE_QSORT_R=on ;; + "--disable-qsort_r") unset FEATURE_QSORT_R ;; "-"*) echo "unknown option: $ARG"; abort_configure ;; esac done @@ -535,7 +539,7 @@ # dependency memrchr while true do - if $TOOLCHAIN_CC $CFLAGS $LDFLAGS -o /dev/null -D_GNU_SOURCE make/test_memrchr.c > /dev/null 2>&1 ; then + if $TOOLCHAIN_CC $CFLAGS $LDFLAGS -o /dev/null make/test_memrchr.c > /dev/null 2>&1 ; then : else break @@ -590,6 +594,27 @@ dep_checked_no_coverage=1 return 0 } +dependency_error_qsort_r() +{ + print_check_msg "$dep_checked_qsort_r" "checking for qsort_r... " + # dependency qsort_r + while true + do + if $TOOLCHAIN_CC $CFLAGS $LDFLAGS -o /dev/null make/test_qsort_r.c > /dev/null 2>&1 ; then + : + else + break + fi + TEMP_CFLAGS="$TEMP_CFLAGS -DWITH_QSORT_R" + print_check_msg "$dep_checked_qsort_r" "yes\n" + dep_checked_qsort_r=1 + return 1 + done + + print_check_msg "$dep_checked_qsort_r" "no\n" + dep_checked_qsort_r=1 + return 0 +} # start collecting dependency information echo > "$TEMP_DIR/flags.mk" @@ -801,26 +826,6 @@ else : fi -if [ -n "$FEATURE_MEMRCHR" ]; then - # check dependency - if dependency_error_memrchr ; then - # "auto" features can fail and are just disabled in this case - if [ "$FEATURE_MEMRCHR" = "auto" ]; then - DISABLE_FEATURE_MEMRCHR=1 - else - DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED memrchr " - ERROR=1 - fi - fi - if [ -n "$DISABLE_FEATURE_MEMRCHR" ]; then - unset FEATURE_MEMRCHR - fi -fi -if [ -n "$FEATURE_MEMRCHR" ]; then - : -else - : -fi if [ -n "$FEATURE_CXX_TESTS" ]; then # check dependency if dependency_error_cxx ; then @@ -867,6 +872,46 @@ TEMP_CFLAGS="$TEMP_CFLAGS -DCX_NO_SZMUL_BUILTIN" TEMP_CXXFLAGS="$TEMP_CXXFLAGS -DCX_NO_SZMUL_BUILTIN" fi +if [ -n "$FEATURE_MEMRCHR" ]; then + # check dependency + if dependency_error_memrchr ; then + # "auto" features can fail and are just disabled in this case + if [ "$FEATURE_MEMRCHR" = "auto" ]; then + DISABLE_FEATURE_MEMRCHR=1 + else + DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED memrchr " + ERROR=1 + fi + fi + if [ -n "$DISABLE_FEATURE_MEMRCHR" ]; then + unset FEATURE_MEMRCHR + fi +fi +if [ -n "$FEATURE_MEMRCHR" ]; then + : +else + : +fi +if [ -n "$FEATURE_QSORT_R" ]; then + # check dependency + if dependency_error_qsort_r ; then + # "auto" features can fail and are just disabled in this case + if [ "$FEATURE_QSORT_R" = "auto" ]; then + DISABLE_FEATURE_QSORT_R=1 + else + DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED qsort_r " + ERROR=1 + fi + fi + if [ -n "$DISABLE_FEATURE_QSORT_R" ]; then + unset FEATURE_QSORT_R + fi +fi +if [ -n "$FEATURE_QSORT_R" ]; then + : +else + : +fi if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then @@ -938,12 +983,6 @@ else echo 'off' fi -printf ' %-16s' 'memrchr:' -if [ -n "$FEATURE_MEMRCHR" ]; then - echo 'on' -else - echo 'off' -fi printf ' %-16s' 'cxx-tests:' if [ -n "$FEATURE_CXX_TESTS" ]; then echo 'on' @@ -956,6 +995,18 @@ else echo 'off' fi +printf ' %-16s' 'memrchr:' +if [ -n "$FEATURE_MEMRCHR" ]; then + echo 'on' +else + echo 'off' +fi +printf ' %-16s' 'qsort_r:' +if [ -n "$FEATURE_QSORT_R" ]; then + echo 'on' +else + echo 'off' +fi echo # generate the config.mk file diff -r 0db02ab1457c -r bf5d647f939d make/project.xml --- a/make/project.xml Wed Dec 17 20:13:08 2025 +0100 +++ b/make/project.xml Thu Dec 18 12:11:30 2025 +0100 @@ -99,10 +99,15 @@ - $TOOLCHAIN_CC $CFLAGS $LDFLAGS -o /dev/null -D_GNU_SOURCE make/test_memrchr.c + $TOOLCHAIN_CC $CFLAGS $LDFLAGS -o /dev/null make/test_memrchr.c -DWITH_MEMRCHR + + $TOOLCHAIN_CC $CFLAGS $LDFLAGS -o /dev/null make/test_qsort_r.c + -DWITH_QSORT_R + + doxygen @@ -120,9 +125,6 @@ address sanitizer asan - - memrchr - cxx the check-cxx makefile target @@ -138,5 +140,11 @@ + + memrchr + + + qsort_r + diff -r 0db02ab1457c -r bf5d647f939d make/test_memrchr.c --- a/make/test_memrchr.c Wed Dec 17 20:13:08 2025 +0100 +++ b/make/test_memrchr.c Thu Dec 18 12:11:30 2025 +0100 @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include int main() { diff -r 0db02ab1457c -r bf5d647f939d make/test_qsort_r.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/test_qsort_r.c Thu Dec 18 12:11:30 2025 +0100 @@ -0,0 +1,14 @@ +#define _GNU_SOURCE +#include + +static int f(const void *a, const void *b, void *c) { + return 0; // not important what it does for this test +} + +int main() { + int x[4] = {3, 1, 4, 0}; + int z = 47; + void *c = &z; + qsort_r(x, 4, sizeof(int), f, c); + return 0; +} diff -r 0db02ab1457c -r bf5d647f939d src/array_list.c --- a/src/array_list.c Wed Dec 17 20:13:08 2025 +0100 +++ b/src/array_list.c Thu Dec 18 12:11:30 2025 +0100 @@ -26,6 +26,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef WITH_MEMRCHR +#define _GNU_SOURCE +#endif + #include "cx/array_list.h" #include "cx/compare.h" #include @@ -349,6 +353,25 @@ n, allow_duplicates, cx_acmp_wrap, &wrapper); } +#ifndef WITH_QSORT_R +static thread_local cx_compare_func2 cx_array_fn_for_qsort; +static thread_local void *cx_array_context_for_qsort; +static int cx_array_qsort_wrapper(const void *l, const void *r) { + return cx_array_fn_for_qsort(l, r, cx_array_context_for_qsort); +} +#endif + +void cx_array_qsort_c(void *array, size_t nmemb, size_t size, + cx_compare_func2 fn, void *context) { +#ifdef WITH_QSORT_R + qsort_r(array, nmemb, size, fn, context); +#else + cx_array_fn_for_qsort = fn; + cx_array_context_for_qsort = context; + qsort(array, nmemb, size, cx_array_qsort_wrapper); +#endif +} + CxIterator cx_array_iterator_(CxArray *array, size_t elem_size) { return cxIterator(array->data, elem_size, array->size); } @@ -849,19 +872,12 @@ return list->collection.size; } -// TODO: remove this hack once we have a solution for qsort() / qsort_s() -static _Thread_local CxList *cx_hack_for_qsort_list; -static int cx_hack_cmp_for_qsort(const void *l, const void *r) { - return cx_list_compare_wrapper(l, r, cx_hack_for_qsort_list); -} - static void cx_arl_sort(struct cx_list_s *list) { - // TODO: think about if we can somehow use qsort()_s - cx_hack_for_qsort_list = list; - qsort(((cx_array_list *) list)->data, + cx_array_qsort_c(((cx_array_list *) list)->data, list->collection.size, list->collection.elem_size, - cx_hack_cmp_for_qsort + cx_list_compare_wrapper, + list ); } diff -r 0db02ab1457c -r bf5d647f939d src/cx/array_list.h --- a/src/cx/array_list.h Wed Dec 17 20:13:08 2025 +0100 +++ b/src/cx/array_list.h Thu Dec 18 12:11:30 2025 +0100 @@ -546,6 +546,20 @@ cx_array_insert_unique_array_a(cxDefaultAllocator, array, cmp_func, sorted_data, n) /** + * An alternative to qsort_r() when that is not available on your platform. + * + * If it is available, qsort_r() is used directly. + * + * @param array the array that shall be sorted + * @param nmemb the number of elements in the array + * @param size the size of one element + * @param fn the compare function + * @param context the context for the compare function + */ +CX_EXPORT void cx_array_qsort_c(void *array, size_t nmemb, size_t size, + cx_compare_func2 fn, void *context); + +/** * Creates an iterator over the elements of an array. * * Internal function - do not use. diff -r 0db02ab1457c -r bf5d647f939d src/cx/list.h --- a/src/cx/list.h Wed Dec 17 20:13:08 2025 +0100 +++ b/src/cx/list.h Thu Dec 18 12:11:30 2025 +0100 @@ -287,11 +287,8 @@ * The purpose of this function is to be called in the initialization code * of your list to set certain members correctly. * - * This is particularly important when you want your list to support - * #CX_STORE_POINTERS as @p elem_size. This function will wrap the list - * class accordingly and make sure that you can implement your list as if - * it was only storing objects, and the wrapper will automatically enable - * the feature of storing pointers. + * This is particularly useful when you want your list to support + * #CX_STORE_POINTERS as @p elem_size. * * @par Example * diff -r 0db02ab1457c -r bf5d647f939d src/list.c --- a/src/list.c Wed Dec 17 20:13:08 2025 +0100 +++ b/src/list.c Thu Dec 18 12:11:30 2025 +0100 @@ -31,6 +31,12 @@ #include #include +// we don't want to include the full array_list.h. +// therefore, we only forward declare the one function we want to use +CX_EXPORT void cx_array_qsort_c(void *array, size_t nmemb, size_t size, + cx_compare_func2 fn, void *context); + + int cx_list_compare_wrapper(const void *l, const void *r, void *c) { CxList *list = c; const void *left; @@ -283,12 +289,6 @@ return cx_list_default_insert_sorted_impl(list, sorted_data, n, false); } -// TODO: remove this hack once we have a solution for qsort() / qsort_s() -static _Thread_local CxList *cx_hack_for_qsort_list; -static int cx_hack_cmp_for_qsort(const void *l, const void *r) { - return cx_list_compare_wrapper(l, r, cx_hack_for_qsort_list); -} - void cx_list_default_sort(struct cx_list_s *list) { size_t elem_size = list->collection.elem_size; size_t list_size = list->collection.size; @@ -304,9 +304,7 @@ } // qsort - // TODO: qsort_s() is not as nice as we thought - cx_hack_for_qsort_list = list; - qsort(tmp, list_size, elem_size, cx_hack_cmp_for_qsort); + cx_array_qsort_c(tmp, list_size, elem_size, cx_list_compare_wrapper, list); // copy elements back loc = tmp; diff -r 0db02ab1457c -r bf5d647f939d src/string.c --- a/src/string.c Wed Dec 17 20:13:08 2025 +0100 +++ b/src/string.c Thu Dec 18 12:11:30 2025 +0100 @@ -26,8 +26,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ -// for memrchr in glibc +#ifdef WITH_MEMRCHR #define _GNU_SOURCE +#endif #include "cx/string.h"