From: Nick Alcock Date: Mon, 3 Jun 2019 13:02:09 +0000 (+0100) Subject: libctf: look for BSD versus GNU qsort_r signatures X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6b22174ff163b828a2069f22353018ca97fc0fb4;p=binutils-gdb.git libctf: look for BSD versus GNU qsort_r signatures We cannot just look for any declaration of qsort_r, because some operating systems have a qsort_r that has a different prototype but which still has a pair of pointers in the right places (the last two args are interchanged): so use AC_LINK_IFELSE to check for both known variants of qsort_r(), and swap their args into a consistent order in a suitable inline function. (The code for this is taken almost unchanged from gnulib.) (Now we are not using AC_LIBOBJ any more, we can use a better name for the qsort_r replacement as well.) libctf/ * qsort_r.c: Rename to... * ctf-qsort_r.c: ... this. (_quicksort): Define to ctf_qsort_r. * ctf-decls.h (qsort_r): Remove. (ctf_qsort_r): Add. (struct ctf_qsort_arg): New, transport the real ARG and COMPAR. (ctf_qsort_compar_thunk): Rearrange the arguments to COMPAR. * Makefile.am (libctf_a_LIBADD): Remove. (libctf_a_SOURCES): New, add ctf-qsort_r.c. * ctf-archive.c (ctf_arc_write): Call ctf_qsort_r, not qsort_r. * ctf-create.c (ctf_update): Likewise. * configure.ac: Check for BSD versus GNU qsort_r signature. * Makefile.in: Regenerate. * config.h.in: Likewise. * configure: Likewise. --- diff --git a/libctf/ChangeLog b/libctf/ChangeLog index 01b8d8da248..c51efea8bcd 100644 --- a/libctf/ChangeLog +++ b/libctf/ChangeLog @@ -1,3 +1,21 @@ +2019-06-04 Nick Alcock + + * qsort_r.c: Rename to... + * ctf-qsort_r.c: ... this. + (_quicksort): Define to ctf_qsort_r. + * ctf-decls.h (qsort_r): Remove. + (ctf_qsort_r): Add. + (struct ctf_qsort_arg): New, transport the real ARG and COMPAR. + (ctf_qsort_compar_thunk): Rearrange the arguments to COMPAR. + * Makefile.am (libctf_a_LIBADD): Remove. + (libctf_a_SOURCES): New, add ctf-qsort_r.c. + * ctf-archive.c (ctf_arc_write): Call ctf_qsort_r, not qsort_r. + * ctf-create.c (ctf_update): Likewise. + * configure.ac: Check for BSD versus GNU qsort_r signature. + * Makefile.in: Regenerate. + * config.h.in: Likewise. + * configure: Likewise. + 2019-06-03 Nick Alcock * ctf-dump.c (ctf_dump_funcs): Free in the right place. diff --git a/libctf/Makefile.am b/libctf/Makefile.am index 49c9f5280a9..926c9919c56 100644 --- a/libctf/Makefile.am +++ b/libctf/Makefile.am @@ -35,4 +35,6 @@ noinst_LIBRARIES = libctf.a libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c ctf-error.c \ ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c ctf-open-bfd.c \ ctf-subr.c ctf-types.c ctf-util.c -libctf_a_LIBADD = $(LIBOBJS) +if NEED_CTF_QSORT_R +libctf_a_SOURCES += ctf-qsort_r.c +endif diff --git a/libctf/Makefile.in b/libctf/Makefile.in index c2cada66167..4fea156c44e 100644 --- a/libctf/Makefile.in +++ b/libctf/Makefile.in @@ -104,6 +104,7 @@ POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : +@NEED_CTF_QSORT_R_TRUE@am__append_1 = ctf-qsort_r.c subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \ @@ -128,12 +129,17 @@ am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libctf_a_AR = $(AR) $(ARFLAGS) -libctf_a_DEPENDENCIES = $(LIBOBJS) +libctf_a_LIBADD = +am__libctf_a_SOURCES_DIST = ctf-archive.c ctf-dump.c ctf-create.c \ + ctf-decl.c ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c \ + ctf-open.c ctf-open-bfd.c ctf-subr.c ctf-types.c ctf-util.c \ + ctf-qsort_r.c +@NEED_CTF_QSORT_R_TRUE@am__objects_1 = ctf-qsort_r.$(OBJEXT) am_libctf_a_OBJECTS = ctf-archive.$(OBJEXT) ctf-dump.$(OBJEXT) \ ctf-create.$(OBJEXT) ctf-decl.$(OBJEXT) ctf-error.$(OBJEXT) \ ctf-hash.$(OBJEXT) ctf-labels.$(OBJEXT) ctf-lookup.$(OBJEXT) \ ctf-open.$(OBJEXT) ctf-open-bfd.$(OBJEXT) ctf-subr.$(OBJEXT) \ - ctf-types.$(OBJEXT) ctf-util.$(OBJEXT) + ctf-types.$(OBJEXT) ctf-util.$(OBJEXT) $(am__objects_1) libctf_a_OBJECTS = $(am_libctf_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -164,7 +170,7 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libctf_a_SOURCES) -DIST_SOURCES = $(libctf_a_SOURCES) +DIST_SOURCES = $(am__libctf_a_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -196,7 +202,7 @@ am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(top_srcdir)/../ar-lib $(top_srcdir)/../compile \ $(top_srcdir)/../depcomp $(top_srcdir)/../install-sh \ $(top_srcdir)/../missing $(top_srcdir)/../mkinstalldirs \ - ChangeLog qsort_r.c + ChangeLog DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -323,11 +329,10 @@ ZLIBINC = @zlibinc@ AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir) -I$(top_srcdir)/../include -I$(top_srcdir)/../bfd -I../bfd AM_CFLAGS = -std=gnu99 @ac_libctf_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @WERROR@ $(ZLIBINC) noinst_LIBRARIES = libctf.a -libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c ctf-error.c \ - ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c ctf-open-bfd.c \ - ctf-subr.c ctf-types.c ctf-util.c - -libctf_a_LIBADD = $(LIBOBJS) +libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c \ + ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c \ + ctf-open-bfd.c ctf-subr.c ctf-types.c ctf-util.c \ + $(am__append_1) all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am @@ -396,7 +401,6 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/qsort_r.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-archive.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-create.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-decl.Po@am__quote@ @@ -407,6 +411,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-lookup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open-bfd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-qsort_r.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-subr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-types.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-util.Po@am__quote@ @@ -687,7 +692,7 @@ clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-tags @@ -735,7 +740,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/libctf/config.h.in b/libctf/config.h.in index 0ecb5bb7211..d81c500c5c3 100644 --- a/libctf/config.h.in +++ b/libctf/config.h.in @@ -9,10 +9,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_BYTESWAP_H -/* Define to 1 if you have the declaration of `qsort_r', and to 0 if you - don't. */ -#undef HAVE_DECL_QSORT_R - /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H @@ -31,6 +27,15 @@ /* Define to 1 if you have the `pread' function. */ #undef HAVE_PREAD +/* Define to 1 if you have the `qsort_r' function. */ +#undef HAVE_QSORT_R + +/* Whether a qsort_r exists with a void *arg as its last arg. */ +#undef HAVE_QSORT_R_ARG_LAST + +/* Whether a qsort_r exists with the compar function as its last arg. */ +#undef HAVE_QSORT_R_COMPAR_LAST + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H diff --git a/libctf/configure b/libctf/configure index 4fb44eb2cc6..d485b1a7cec 100755 --- a/libctf/configure +++ b/libctf/configure @@ -620,10 +620,13 @@ ac_includes_default="\ #endif" ac_header_list= +ac_func_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +NEED_CTF_QSORT_R_FALSE +NEED_CTF_QSORT_R_TRUE zlibinc zlibdir ac_libctf_warn_cflags @@ -1809,52 +1812,6 @@ $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func - -# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES -# --------------------------------------------- -# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR -# accordingly. -ac_fn_c_check_decl () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - as_decl_name=`echo $2|sed 's/ *(.*//'` - as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 -$as_echo_n "checking whether $as_decl_name is declared... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -#ifndef $as_decl_name -#ifdef __cplusplus - (void) $as_decl_use; -#else - (void) $as_decl_name; -#endif -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. @@ -2142,6 +2099,7 @@ fi as_fn_append ac_header_list " stdlib.h" as_fn_append ac_header_list " unistd.h" as_fn_append ac_header_list " sys/param.h" +as_fn_append ac_func_list " qsort_r" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false @@ -6401,23 +6359,108 @@ _ACEOF fi done -ac_fn_c_check_decl "$LINENO" "qsort_r" "ac_cv_have_decl_qsort_r" "$ac_includes_default" -if test "x$ac_cv_have_decl_qsort_r" = xyes; then : - ac_have_decl=1 -else - ac_have_decl=0 + + + + + for ac_func in $ac_func_list +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + fi +done -cat >>confdefs.h <<_ACEOF -#define HAVE_DECL_QSORT_R $ac_have_decl + + + +if test $ac_cv_func_qsort_r = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for qsort_r signature" >&5 +$as_echo_n "checking for qsort_r signature... " >&6; } +if ${ac_cv_libctf_qsort_r_signature+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#undef qsort_r + #include + void qsort_r (void *, size_t, size_t, + int (*) (void const *, void const *, + void *), + void *); + void (*p) (void *, size_t, size_t, + int (*) (void const *, void const *, + void *), + void *) = qsort_r; + +int +main () +{ + + ; + return 0; +} _ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_libctf_qsort_r_signature=GNU +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#undef qsort_r + #include + void qsort_r (void *, size_t, size_t, void *, + int (*) (void *, + void const *, + void const *)); + void (*p) (void *, size_t, size_t, void *, + int (*) (void *, void const *, + void const *)) = qsort_r; -case " $LIBOBJS " in - *" qsort_r.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS qsort_r.$ac_objext" - ;; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_libctf_qsort_r_signature=BSD +else + ac_cv_libctf_qsort_r_signature=unknown +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libctf_qsort_r_signature" >&5 +$as_echo "$ac_cv_libctf_qsort_r_signature" >&6; } +fi + +case x$ac_cv_libctf_qsort_r_signature in + xGNU) +$as_echo "#define HAVE_QSORT_R_ARG_LAST 1" >>confdefs.h +;; + xBSD) +$as_echo "#define HAVE_QSORT_R_COMPAR_LAST 1" >>confdefs.h +;; + *) ac_cv_libctf_qsort_r_signature=unknown;; esac + if test "${ac_cv_libctf_qsort_r_signature}" = unknown; then + NEED_CTF_QSORT_R_TRUE= + NEED_CTF_QSORT_R_FALSE='#' +else + NEED_CTF_QSORT_R_TRUE='#' + NEED_CTF_QSORT_R_FALSE= +fi + ac_config_files="$ac_config_files Makefile" @@ -6561,6 +6604,10 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${NEED_CTF_QSORT_R_TRUE}" && test -z "${NEED_CTF_QSORT_R_FALSE}"; then + as_fn_error $? "conditional \"NEED_CTF_QSORT_R\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 diff --git a/libctf/configure.ac b/libctf/configure.ac index 8fd5388d2a0..beb90ba75cc 100644 --- a/libctf/configure.ac +++ b/libctf/configure.ac @@ -90,8 +90,48 @@ fi AC_C_BIGENDIAN AC_CHECK_HEADERS(byteswap.h endian.h) AC_CHECK_FUNCS(pread) -AC_CHECK_DECLS([qsort_r]) -AC_LIBOBJ([qsort_r]) + +dnl Check for qsort_r. (Taken from gnulib.) +AC_CHECK_FUNCS_ONCE([qsort_r]) +if test $ac_cv_func_qsort_r = yes; then + AC_CACHE_CHECK([for qsort_r signature], [ac_cv_libctf_qsort_r_signature], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#undef qsort_r + #include + void qsort_r (void *, size_t, size_t, + int (*) (void const *, void const *, + void *), + void *); + void (*p) (void *, size_t, size_t, + int (*) (void const *, void const *, + void *), + void *) = qsort_r; + ]])], + [ac_cv_libctf_qsort_r_signature=GNU], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#undef qsort_r + #include + void qsort_r (void *, size_t, size_t, void *, + int (*) (void *, + void const *, + void const *)); + void (*p) (void *, size_t, size_t, void *, + int (*) (void *, void const *, + void const *)) = qsort_r; + ]])], + [ac_cv_libctf_qsort_r_signature=BSD], + [ac_cv_libctf_qsort_r_signature=unknown])])]) +fi + +case x$ac_cv_libctf_qsort_r_signature in + xGNU) AC_DEFINE([HAVE_QSORT_R_ARG_LAST], 1, + [Whether a qsort_r exists with a void *arg as its last arg.]);; + xBSD) AC_DEFINE([HAVE_QSORT_R_COMPAR_LAST], 1, + [Whether a qsort_r exists with the compar function as its last arg.]);; + *) ac_cv_libctf_qsort_r_signature=unknown;; +esac + +AM_CONDITIONAL(NEED_CTF_QSORT_R, test "${ac_cv_libctf_qsort_r_signature}" = unknown) AC_CONFIG_FILES(Makefile) AC_CONFIG_HEADERS(config.h) diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c index a238edb66ba..90cd020b781 100644 --- a/libctf/ctf-archive.c +++ b/libctf/ctf-archive.c @@ -169,10 +169,11 @@ ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt, modent++; } - qsort_r ((ctf_archive_modent_t *) ((char *) archdr - + sizeof (struct ctf_archive)), - le64toh (archdr->ctfa_nfiles), - sizeof (struct ctf_archive_modent), sort_modent_by_name, nametbl); + ctf_qsort_r ((ctf_archive_modent_t *) ((char *) archdr + + sizeof (struct ctf_archive)), + le64toh (archdr->ctfa_nfiles), + sizeof (struct ctf_archive_modent), sort_modent_by_name, + nametbl); /* Now the name table. */ diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c index 227f62d8fd7..3beed887122 100644 --- a/libctf/ctf-create.c +++ b/libctf/ctf-create.c @@ -344,7 +344,7 @@ ctf_update (ctf_file_t *fp) } assert (i == nvars); - qsort_r (dvarents, nvars, sizeof (ctf_varent_t), ctf_sort_var, s0); + ctf_qsort_r (dvarents, nvars, sizeof (ctf_varent_t), ctf_sort_var, s0); t += sizeof (ctf_varent_t) * nvars; assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_typeoff); diff --git a/libctf/ctf-decls.h b/libctf/ctf-decls.h index 5e9ede48099..d12409e4b60 100644 --- a/libctf/ctf-decls.h +++ b/libctf/ctf-decls.h @@ -22,12 +22,45 @@ #include "config.h" -#if !HAVE_DECL_QSORT_R #include -void qsort_r (void *base, size_t nmemb, size_t size, +#include + +#if HAVE_QSORT_R_ARG_LAST +static inline void +ctf_qsort_r (void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *, void *), + void *arg) +{ + qsort_r (base, nmemb, size, compar, arg); +} +#elif HAVE_QSORT_R_COMPAR_LAST +struct ctf_qsort_arg +{ + int (*compar) (const void *, const void *, void *); + void *arg; +}; + +static int +ctf_qsort_compar_thunk (void *arg, const void *a, const void *b) +{ + struct ctf_qsort_arg *qsort_arg = (struct ctf_qsort_arg *) arg; + + return qsort_arg->compar (a, b, arg); +} + +static inline void +ctf_qsort_r (void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *, void *), + void *arg) +{ + struct ctf_qsort_arg thunk = { compar, arg }; + qsort_r (base, nmemb, size, &thunk, ctf_qsort_compar_thunk); +} +#else +void ctf_qsort_r (void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg); -#endif /* !HAVE_DECL_QSORT_R */ +#endif #undef MAX #undef MIN diff --git a/libctf/ctf-qsort_r.c b/libctf/ctf-qsort_r.c new file mode 100644 index 00000000000..6cb221d6f54 --- /dev/null +++ b/libctf/ctf-qsort_r.c @@ -0,0 +1,259 @@ +/* Copyright (C) 1991-2019 Free Software Foundation, Inc. + This file is part of libctf (imported from Gnulib). + Written by Douglas C. Schmidt (schmidt@ics.uci.edu). + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* If you consider tuning this algorithm, you should consult first: + Engineering a sort function; Jon Bentley and M. Douglas McIlroy; + Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */ + +#ifndef _LIBC +# include +#endif + +#include +#include +#include +#include "ctf-decls.h" + +#ifndef _LIBC +# define _quicksort ctf_qsort_r +# define __compar_d_fn_t compar_d_fn_t +typedef int (*compar_d_fn_t) (const void *, const void *, void *); +#endif + +/* Byte-wise swap two items of size SIZE. */ +#define SWAP(a, b, size) \ + do \ + { \ + size_t __size = (size); \ + char *__a = (a), *__b = (b); \ + do \ + { \ + char __tmp = *__a; \ + *__a++ = *__b; \ + *__b++ = __tmp; \ + } while (--__size > 0); \ + } while (0) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define MAX_THRESH 4 + +/* Stack node declarations used to store unfulfilled partition obligations. */ +typedef struct + { + char *lo; + char *hi; + } stack_node; + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ +/* The stack needs log (total_elements) entries (we could even subtract + log(MAX_THRESH)). Since total_elements has type size_t, we get as + upper bound for log (total_elements): + bits per byte (CHAR_BIT) * sizeof(size_t). */ +#define STACK_SIZE (CHAR_BIT * sizeof(size_t)) +#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top)) +#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi))) +#define STACK_NOT_EMPTY (stack < top) + + +/* Order size using quicksort. This implementation incorporates + four optimizations discussed in Sedgewick: + + 1. Non-recursive, using an explicit stack of pointer that store the + next array partition to sort. To save time, this maximum amount + of space required to store an array of SIZE_MAX is allocated on the + stack. Assuming a 32-bit (64 bit) integer for size_t, this needs + only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). + Pretty cheap, actually. + + 2. Chose the pivot element using a median-of-three decision tree. + This reduces the probability of selecting a bad pivot value and + eliminates certain extraneous comparisons. + + 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving + insertion sort to order the MAX_THRESH items within each partition. + This is a big win, since insertion sort is faster for small, mostly + sorted array segments. + + 4. The larger of the two sub-partitions is always pushed onto the + stack first, with the algorithm then concentrating on the + smaller partition. This *guarantees* no more than log (total_elems) + stack size is needed (actually O(1) in this case)! */ + +void +_quicksort (void *const pbase, size_t total_elems, size_t size, + __compar_d_fn_t cmp, void *arg) +{ + char *base_ptr = (char *) pbase; + + const size_t max_thresh = MAX_THRESH * size; + + if (total_elems == 0) + /* Avoid lossage with unsigned arithmetic below. */ + return; + + if (total_elems > MAX_THRESH) + { + char *lo = base_ptr; + char *hi = &lo[size * (total_elems - 1)]; + stack_node stack[STACK_SIZE]; + stack_node *top = stack; + + PUSH (NULL, NULL); + + while (STACK_NOT_EMPTY) + { + char *left_ptr; + char *right_ptr; + + /* Select median value from among LO, MID, and HI. Rearrange + LO and HI so the three values are sorted. This lowers the + probability of picking a pathological pivot value and + skips a comparison for both the LEFT_PTR and RIGHT_PTR in + the while loops. */ + + char *mid = lo + size * ((hi - lo) / size >> 1); + + if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) + SWAP (mid, lo, size); + if ((*cmp) ((void *) hi, (void *) mid, arg) < 0) + SWAP (mid, hi, size); + else + goto jump_over; + if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) + SWAP (mid, lo, size); + jump_over:; + + left_ptr = lo + size; + right_ptr = hi - size; + + /* Here's the famous ``collapse the walls'' section of quicksort. + Gotta like those tight inner loops! They are the main reason + that this algorithm runs much faster than others. */ + do + { + while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0) + left_ptr += size; + + while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0) + right_ptr -= size; + + if (left_ptr < right_ptr) + { + SWAP (left_ptr, right_ptr, size); + if (mid == left_ptr) + mid = right_ptr; + else if (mid == right_ptr) + mid = left_ptr; + left_ptr += size; + right_ptr -= size; + } + else if (left_ptr == right_ptr) + { + left_ptr += size; + right_ptr -= size; + break; + } + } + while (left_ptr <= right_ptr); + + /* Set up pointers for next iteration. First determine whether + left and right partitions are below the threshold size. If so, + ignore one or both. Otherwise, push the larger partition's + bounds on the stack and continue sorting the smaller one. */ + + if ((size_t) (right_ptr - lo) <= max_thresh) + { + if ((size_t) (hi - left_ptr) <= max_thresh) + /* Ignore both small partitions. */ + POP (lo, hi); + else + /* Ignore small left partition. */ + lo = left_ptr; + } + else if ((size_t) (hi - left_ptr) <= max_thresh) + /* Ignore small right partition. */ + hi = right_ptr; + else if ((right_ptr - lo) > (hi - left_ptr)) + { + /* Push larger left partition indices. */ + PUSH (lo, right_ptr); + lo = left_ptr; + } + else + { + /* Push larger right partition indices. */ + PUSH (left_ptr, hi); + hi = right_ptr; + } + } + } + + /* Once the BASE_PTR array is partially sorted by quicksort the rest + is completely sorted using insertion sort, since this is efficient + for partitions below MAX_THRESH size. BASE_PTR points to the beginning + of the array to sort, and END_PTR points at the very last element in + the array (*not* one beyond it!). */ + +#define min(x, y) ((x) < (y) ? (x) : (y)) + + { + char *const end_ptr = &base_ptr[size * (total_elems - 1)]; + char *tmp_ptr = base_ptr; + char *thresh = min(end_ptr, base_ptr + max_thresh); + char *run_ptr; + + /* Find smallest element in first threshold and place it at the + array's beginning. This is the smallest array element, + and the operation speeds up insertion sort's inner loop. */ + + for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size) + if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) + tmp_ptr = run_ptr; + + if (tmp_ptr != base_ptr) + SWAP (tmp_ptr, base_ptr, size); + + /* Insertion sort, running from left-hand-side up to right-hand-side. */ + + run_ptr = base_ptr + size; + while ((run_ptr += size) <= end_ptr) + { + tmp_ptr = run_ptr - size; + while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) + tmp_ptr -= size; + + tmp_ptr += size; + if (tmp_ptr != run_ptr) + { + char *trav; + + trav = run_ptr + size; + while (--trav >= run_ptr) + { + char c = *trav; + char *hi, *lo; + + for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo) + *hi = *lo; + *hi = c; + } + } + } + } +} diff --git a/libctf/qsort_r.c b/libctf/qsort_r.c deleted file mode 100644 index 6c334fdaf39..00000000000 --- a/libctf/qsort_r.c +++ /dev/null @@ -1,259 +0,0 @@ -/* Copyright (C) 1991-2019 Free Software Foundation, Inc. - This file is part of libctf (imported from Gnulib). - Written by Douglas C. Schmidt (schmidt@ics.uci.edu). - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ - -/* If you consider tuning this algorithm, you should consult first: - Engineering a sort function; Jon Bentley and M. Douglas McIlroy; - Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */ - -#ifndef _LIBC -# include -#endif - -#include -#include -#include -#include "ctf-decls.h" - -#ifndef _LIBC -# define _quicksort qsort_r -# define __compar_d_fn_t compar_d_fn_t -typedef int (*compar_d_fn_t) (const void *, const void *, void *); -#endif - -/* Byte-wise swap two items of size SIZE. */ -#define SWAP(a, b, size) \ - do \ - { \ - size_t __size = (size); \ - char *__a = (a), *__b = (b); \ - do \ - { \ - char __tmp = *__a; \ - *__a++ = *__b; \ - *__b++ = __tmp; \ - } while (--__size > 0); \ - } while (0) - -/* Discontinue quicksort algorithm when partition gets below this size. - This particular magic number was chosen to work best on a Sun 4/260. */ -#define MAX_THRESH 4 - -/* Stack node declarations used to store unfulfilled partition obligations. */ -typedef struct - { - char *lo; - char *hi; - } stack_node; - -/* The next 4 #defines implement a very fast in-line stack abstraction. */ -/* The stack needs log (total_elements) entries (we could even subtract - log(MAX_THRESH)). Since total_elements has type size_t, we get as - upper bound for log (total_elements): - bits per byte (CHAR_BIT) * sizeof(size_t). */ -#define STACK_SIZE (CHAR_BIT * sizeof(size_t)) -#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top)) -#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi))) -#define STACK_NOT_EMPTY (stack < top) - - -/* Order size using quicksort. This implementation incorporates - four optimizations discussed in Sedgewick: - - 1. Non-recursive, using an explicit stack of pointer that store the - next array partition to sort. To save time, this maximum amount - of space required to store an array of SIZE_MAX is allocated on the - stack. Assuming a 32-bit (64 bit) integer for size_t, this needs - only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). - Pretty cheap, actually. - - 2. Chose the pivot element using a median-of-three decision tree. - This reduces the probability of selecting a bad pivot value and - eliminates certain extraneous comparisons. - - 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving - insertion sort to order the MAX_THRESH items within each partition. - This is a big win, since insertion sort is faster for small, mostly - sorted array segments. - - 4. The larger of the two sub-partitions is always pushed onto the - stack first, with the algorithm then concentrating on the - smaller partition. This *guarantees* no more than log (total_elems) - stack size is needed (actually O(1) in this case)! */ - -void -_quicksort (void *const pbase, size_t total_elems, size_t size, - __compar_d_fn_t cmp, void *arg) -{ - char *base_ptr = (char *) pbase; - - const size_t max_thresh = MAX_THRESH * size; - - if (total_elems == 0) - /* Avoid lossage with unsigned arithmetic below. */ - return; - - if (total_elems > MAX_THRESH) - { - char *lo = base_ptr; - char *hi = &lo[size * (total_elems - 1)]; - stack_node stack[STACK_SIZE]; - stack_node *top = stack; - - PUSH (NULL, NULL); - - while (STACK_NOT_EMPTY) - { - char *left_ptr; - char *right_ptr; - - /* Select median value from among LO, MID, and HI. Rearrange - LO and HI so the three values are sorted. This lowers the - probability of picking a pathological pivot value and - skips a comparison for both the LEFT_PTR and RIGHT_PTR in - the while loops. */ - - char *mid = lo + size * ((hi - lo) / size >> 1); - - if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) - SWAP (mid, lo, size); - if ((*cmp) ((void *) hi, (void *) mid, arg) < 0) - SWAP (mid, hi, size); - else - goto jump_over; - if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) - SWAP (mid, lo, size); - jump_over:; - - left_ptr = lo + size; - right_ptr = hi - size; - - /* Here's the famous ``collapse the walls'' section of quicksort. - Gotta like those tight inner loops! They are the main reason - that this algorithm runs much faster than others. */ - do - { - while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0) - left_ptr += size; - - while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0) - right_ptr -= size; - - if (left_ptr < right_ptr) - { - SWAP (left_ptr, right_ptr, size); - if (mid == left_ptr) - mid = right_ptr; - else if (mid == right_ptr) - mid = left_ptr; - left_ptr += size; - right_ptr -= size; - } - else if (left_ptr == right_ptr) - { - left_ptr += size; - right_ptr -= size; - break; - } - } - while (left_ptr <= right_ptr); - - /* Set up pointers for next iteration. First determine whether - left and right partitions are below the threshold size. If so, - ignore one or both. Otherwise, push the larger partition's - bounds on the stack and continue sorting the smaller one. */ - - if ((size_t) (right_ptr - lo) <= max_thresh) - { - if ((size_t) (hi - left_ptr) <= max_thresh) - /* Ignore both small partitions. */ - POP (lo, hi); - else - /* Ignore small left partition. */ - lo = left_ptr; - } - else if ((size_t) (hi - left_ptr) <= max_thresh) - /* Ignore small right partition. */ - hi = right_ptr; - else if ((right_ptr - lo) > (hi - left_ptr)) - { - /* Push larger left partition indices. */ - PUSH (lo, right_ptr); - lo = left_ptr; - } - else - { - /* Push larger right partition indices. */ - PUSH (left_ptr, hi); - hi = right_ptr; - } - } - } - - /* Once the BASE_PTR array is partially sorted by quicksort the rest - is completely sorted using insertion sort, since this is efficient - for partitions below MAX_THRESH size. BASE_PTR points to the beginning - of the array to sort, and END_PTR points at the very last element in - the array (*not* one beyond it!). */ - -#define min(x, y) ((x) < (y) ? (x) : (y)) - - { - char *const end_ptr = &base_ptr[size * (total_elems - 1)]; - char *tmp_ptr = base_ptr; - char *thresh = min(end_ptr, base_ptr + max_thresh); - char *run_ptr; - - /* Find smallest element in first threshold and place it at the - array's beginning. This is the smallest array element, - and the operation speeds up insertion sort's inner loop. */ - - for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size) - if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) - tmp_ptr = run_ptr; - - if (tmp_ptr != base_ptr) - SWAP (tmp_ptr, base_ptr, size); - - /* Insertion sort, running from left-hand-side up to right-hand-side. */ - - run_ptr = base_ptr + size; - while ((run_ptr += size) <= end_ptr) - { - tmp_ptr = run_ptr - size; - while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) - tmp_ptr -= size; - - tmp_ptr += size; - if (tmp_ptr != run_ptr) - { - char *trav; - - trav = run_ptr + size; - while (--trav >= run_ptr) - { - char c = *trav; - char *hi, *lo; - - for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo) - *hi = *lo; - *hi = c; - } - } - } - } -}