diff --git a/meson.build b/meson.build index 3389cd3604c..da914c85c30 100644 --- a/meson.build +++ b/meson.build @@ -1310,7 +1310,7 @@ foreach h : ['xlocale.h', 'linux/futex.h', 'endian.h', 'dlfcn.h', 'execinfo.h', endforeach foreach f : ['strtof', 'mkostemp', 'timespec_get', 'memfd_create', 'random_r', - 'flock', 'strtok_r', 'getrandom', 'qsort_r'] + 'flock', 'strtok_r', 'getrandom', 'qsort_r', 'qsort_s'] if cc.has_function(f) pre_args += '-DHAVE_@0@'.format(f.to_upper()) endif diff --git a/src/util/u_qsort.cpp b/src/util/u_qsort.cpp index d6ade16787d..1eb332335af 100644 --- a/src/util/u_qsort.cpp +++ b/src/util/u_qsort.cpp @@ -42,3 +42,11 @@ util_tls_qsort_r(void *base, size_t nmemb, size_t size, tl_qsort_r_arg = arg; return qsort(base, nmemb, size, qsort_r_compar); } + +int +util_qsort_adapter(void *ctx, const void *elem1, const void *elem2) +{ + util_qsort_adapter_data *data = + reinterpret_cast(ctx); + return data->compar(elem1, elem2, data->args); +} diff --git a/src/util/u_qsort.h b/src/util/u_qsort.h index 61074bd95fb..34fff94dba4 100644 --- a/src/util/u_qsort.h +++ b/src/util/u_qsort.h @@ -36,14 +36,56 @@ void util_tls_qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg); + +struct util_qsort_adapter_data { + int (*compar)(const void*, const void*, void*); + void *args; +}; + +/** + * Converts comparison function arguments + * from [MSVC, BSD, macOS] + * (void *ctx, const void *elem1, const void *elem2) + * to [GNU, C11] + * (const void *elem1, const void *elem2, void *ctx); + */ +int util_qsort_adapter(void *ctx, const void *elem1, const void *elem2); + static inline void util_qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg) { -#if HAVE_QSORT_R && !(DETECT_OS_APPLE || DETECT_OS_BSD) +#if HAVE_QSORT_R +# if DETECT_OS_APPLE || DETECT_OS_BSD + /* BSD/macOS qsort_r takes "arg" before the comparison function and it + * pass the "arg" before the elements. + */ + struct util_qsort_adapter_data data = { + compar, + arg + }; + qsort_r(base, nmemb, size, &data, util_qsort_adapter); +# else + /* GNU extension added in glibc 2.8 */ qsort_r(base, nmemb, size, compar, arg); +# endif +#elif HAVE_QSORT_S +# ifdef _WIN32 + /* MSVC/MinGW qsort_s takes "arg" after the comparison function and it + * pass the "arg" before the elements. + */ + struct util_qsort_adapter_data data = { + compar, + arg + }; + qsort_s(base, nmemb, size, util_qsort_adapter, &data); +# else + /* C11 added qsort_s */ + qsort_s(base, nmemb, size, compar, arg); +# endif #else + /* Fall-back to using thread local storage */ util_tls_qsort_r(base, nmemb, size, compar, arg); #endif }