Whamcloud - gitweb
Provide and use sort_r() instead of qsort_r() for portability reasons
authorTheodore Ts'o <tytso@mit.edu>
Tue, 9 Feb 2021 20:27:51 +0000 (15:27 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 9 Feb 2021 20:27:51 +0000 (15:27 -0500)
The qsort_r() function is specific to glibc.  It is not present in the
musl C library.  Worse, FreeBSD supports qsort_r, but with an
incompatible interface.  So use sort_r() from commit c8c65c1e183d
from the git repository: https://github.com/noporpoise/sort_r

https://github.com/tytso/e2fsprogs/issues/58

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/rehash.c
lib/support/sort_r.h [new file with mode: 0644]

index 1ff4aa1..204ff7d 100644 (file)
@@ -51,6 +51,7 @@
 #include <errno.h>
 #include "e2fsck.h"
 #include "problem.h"
+#include "support/sort_r.h"
 
 /* Schedule a dir to be rebuilt during pass 3A. */
 void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
@@ -251,7 +252,8 @@ static int same_name(const struct name_cmp_ctx *cmp_ctx, char *s1,
                return (len1 == len2 && !memcmp(s1, s2, len1));
        else
                return !ext2fs_casefold_cmp(cmp_ctx->tbl,
-                                           s1, len1, s2, len2);
+                                           (unsigned char *) s1, len1,
+                                           (unsigned char *) s2, len2);
 }
 
 /* Used for sorting the hash entry */
@@ -291,8 +293,9 @@ static EXT2_QSORT_TYPE name_cf_cmp(const struct name_cmp_ctx *ctx,
        he_a_len = ext2fs_dirent_name_len(he_a->dir);
        he_b_len = ext2fs_dirent_name_len(he_b->dir);
 
-       ret = ext2fs_casefold_cmp(ctx->tbl, he_a->dir->name, he_a_len,
-                                 he_b->dir->name, he_b_len);
+       ret = ext2fs_casefold_cmp(ctx->tbl,
+                                 (unsigned char *) he_a->dir->name, he_a_len,
+                                 (unsigned char *) he_b->dir->name, he_b_len);
        if (ret == 0) {
                if (he_a_len > he_b_len)
                        ret = 1;
@@ -1048,11 +1051,13 @@ retry_nohash:
        /* Sort the list */
 resort:
        if (fd.compress && fd.num_array > 1)
-               qsort_r(fd.harray+2, fd.num_array-2, sizeof(struct hash_entry),
-                       hash_cmp, &name_cmp_ctx);
+               sort_r_simple(fd.harray+2, fd.num_array-2,
+                             sizeof(struct hash_entry),
+                             hash_cmp, &name_cmp_ctx);
        else
-               qsort_r(fd.harray, fd.num_array, sizeof(struct hash_entry),
-                       hash_cmp, &name_cmp_ctx);
+               sort_r_simple(fd.harray, fd.num_array,
+                             sizeof(struct hash_entry),
+                             hash_cmp, &name_cmp_ctx);
 
        /*
         * Look for duplicates
diff --git a/lib/support/sort_r.h b/lib/support/sort_r.h
new file mode 100644 (file)
index 0000000..dc17e8a
--- /dev/null
@@ -0,0 +1,321 @@
+/* Isaac Turner 29 April 2014 Public Domain */
+#ifndef SORT_R_H_
+#define SORT_R_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+
+sort_r function to be exported.
+
+Parameters:
+  base is the array to be sorted
+  nel is the number of elements in the array
+  width is the size in bytes of each element of the array
+  compar is the comparison function
+  arg is a pointer to be passed to the comparison function
+
+void sort_r(void *base, size_t nel, size_t width,
+            int (*compar)(const void *_a, const void *_b, void *_arg),
+            void *arg);
+
+*/
+
+#define _SORT_R_INLINE inline
+
+#if (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \
+     defined __FreeBSD__ || defined __DragonFly__)
+#  define _SORT_R_BSD
+#elif (defined _GNU_SOURCE || defined __gnu_hurd__ || defined __GNU__ || \
+       defined __linux__ || defined __MINGW32__ || defined __GLIBC__)
+#  define _SORT_R_LINUX
+#elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__)
+#  define _SORT_R_WINDOWS
+#  undef _SORT_R_INLINE
+#  define _SORT_R_INLINE __inline
+#else
+  /* Using our own recursive quicksort sort_r_simple() */
+#endif
+
+#if (defined NESTED_QSORT && NESTED_QSORT == 0)
+#  undef NESTED_QSORT
+#endif
+
+#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
+
+/* swap a and b */
+/* a and b must not be equal! */
+static _SORT_R_INLINE void sort_r_swap(char *__restrict a, char *__restrict b,
+                                       size_t w)
+{
+  char tmp, *end = a+w;
+  for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); }
+}
+
+/* swap a, b iff a>b */
+/* a and b must not be equal! */
+/* __restrict is same as restrict but better support on old machines */
+static _SORT_R_INLINE int sort_r_cmpswap(char *__restrict a,
+                                         char *__restrict b, size_t w,
+                                         int (*compar)(const void *_a,
+                                                       const void *_b,
+                                                       void *_arg),
+                                         void *arg)
+{
+  if(compar(a, b, arg) > 0) {
+    sort_r_swap(a, b, w);
+    return 1;
+  }
+  return 0;
+}
+
+/*
+Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr,
+with the smallest swap so that the blocks are in the opposite order. Blocks may
+be internally re-ordered e.g.
+
+  12345ab  ->   ab34512
+  123abc   ->   abc123
+  12abcde  ->   deabc12
+*/
+static _SORT_R_INLINE void sort_r_swap_blocks(char *ptr, size_t na, size_t nb)
+{
+  if(na > 0 && nb > 0) {
+    if(na > nb) { sort_r_swap(ptr, ptr+na, nb); }
+    else { sort_r_swap(ptr, ptr+nb, na); }
+  }
+}
+
+/* Implement recursive quicksort ourselves */
+/* Note: quicksort is not stable, equivalent values may be swapped */
+static _SORT_R_INLINE void sort_r_simple(void *base, size_t nel, size_t w,
+                                         int (*compar)(const void *_a,
+                                                       const void *_b,
+                                                       void *_arg),
+                                         void *arg)
+{
+  char *b = (char *)base, *end = b + nel*w;
+
+  /* for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
+  printf("\n"); */
+
+  if(nel < 10) {
+    /* Insertion sort for arbitrarily small inputs */
+    char *pi, *pj;
+    for(pi = b+w; pi < end; pi += w) {
+      for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {}
+    }
+  }
+  else
+  {
+    /* nel > 6; Quicksort */
+
+    int cmp;
+    char *pl, *ple, *pr, *pre, *pivot;
+    char *last = b+w*(nel-1), *tmp;
+
+    /*
+    Use median of second, middle and second-last items as pivot.
+    First and last may have been swapped with pivot and therefore be extreme
+    */
+    char *l[3];
+    l[0] = b + w;
+    l[1] = b+w*(nel/2);
+    l[2] = last - w;
+
+    /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */
+
+    if(compar(l[0],l[1],arg) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
+    if(compar(l[1],l[2],arg) > 0) {
+      SORT_R_SWAP(l[1], l[2], tmp);
+      if(compar(l[0],l[1],arg) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
+    }
+
+    /* swap mid value (l[1]), and last element to put pivot as last element */
+    if(l[1] != last) { sort_r_swap(l[1], last, w); }
+
+    /*
+    pl is the next item on the left to be compared to the pivot
+    pr is the last item on the right that was compared to the pivot
+    ple is the left position to put the next item that equals the pivot
+    ple is the last right position where we put an item that equals the pivot
+
+                                           v- end (beyond the array)
+      EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE.
+      ^- b  ^- ple  ^- pl   ^- pr  ^- pre ^- last (where the pivot is)
+
+    Pivot comparison key:
+      E = equal, L = less than, u = unknown, G = greater than, E = equal
+    */
+    pivot = last;
+    ple = pl = b;
+    pre = pr = last;
+
+    /*
+    Strategy:
+    Loop into the list from the left and right at the same time to find:
+    - an item on the left that is greater than the pivot
+    - an item on the right that is less than the pivot
+    Once found, they are swapped and the loop continues.
+    Meanwhile items that are equal to the pivot are moved to the edges of the
+    array.
+    */
+    while(pl < pr) {
+      /* Move left hand items which are equal to the pivot to the far left.
+         break when we find an item that is greater than the pivot */
+      for(; pl < pr; pl += w) {
+        cmp = compar(pl, pivot, arg);
+        if(cmp > 0) { break; }
+        else if(cmp == 0) {
+          if(ple < pl) { sort_r_swap(ple, pl, w); }
+          ple += w;
+        }
+      }
+      /* break if last batch of left hand items were equal to pivot */
+      if(pl >= pr) { break; }
+      /* Move right hand items which are equal to the pivot to the far right.
+         break when we find an item that is less than the pivot */
+      for(; pl < pr; ) {
+        pr -= w; /* Move right pointer onto an unprocessed item */
+        cmp = compar(pr, pivot, arg);
+        if(cmp == 0) {
+          pre -= w;
+          if(pr < pre) { sort_r_swap(pr, pre, w); }
+        }
+        else if(cmp < 0) {
+          if(pl < pr) { sort_r_swap(pl, pr, w); }
+          pl += w;
+          break;
+        }
+      }
+    }
+
+    pl = pr; /* pr may have gone below pl */
+
+    /*
+    Now we need to go from: EEELLLGGGGEEEE
+                        to: LLLEEEEEEEGGGG
+
+    Pivot comparison key:
+      E = equal, L = less than, u = unknown, G = greater than, E = equal
+    */
+    sort_r_swap_blocks(b, ple-b, pl-ple);
+    sort_r_swap_blocks(pr, pre-pr, end-pre);
+
+    /*for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
+    printf("\n");*/
+
+    sort_r_simple(b, (pl-ple)/w, w, compar, arg);
+    sort_r_simple(end-(pre-pr), (pre-pr)/w, w, compar, arg);
+  }
+}
+
+
+#if defined NESTED_QSORT
+
+  static _SORT_R_INLINE void sort_r(void *base, size_t nel, size_t width,
+                                    int (*compar)(const void *_a,
+                                                  const void *_b,
+                                                  void *aarg),
+                                    void *arg)
+  {
+    int nested_cmp(const void *a, const void *b)
+    {
+      return compar(a, b, arg);
+    }
+
+    qsort(base, nel, width, nested_cmp);
+  }
+
+#else /* !NESTED_QSORT */
+
+  /* Declare structs and functions */
+
+  #if defined _SORT_R_BSD
+
+    /* Ensure qsort_r is defined */
+    extern void qsort_r(void *base, size_t nel, size_t width, void *thunk,
+                        int (*compar)(void *_thunk,
+                                      const void *_a, const void *_b));
+
+  #endif
+
+  #if defined _SORT_R_BSD || defined _SORT_R_WINDOWS
+
+    /* BSD (qsort_r), Windows (qsort_s) require argument swap */
+
+    struct sort_r_data
+    {
+      void *arg;
+      int (*compar)(const void *_a, const void *_b, void *_arg);
+    };
+
+    static _SORT_R_INLINE int sort_r_arg_swap(void *s,
+                                              const void *a, const void *b)
+    {
+      struct sort_r_data *ss = (struct sort_r_data*)s;
+      return (ss->compar)(a, b, ss->arg);
+    }
+
+  #endif
+
+  #if defined _SORT_R_LINUX
+
+    typedef int(* __compar_d_fn_t)(const void *, const void *, void *);
+    extern void qsort_r(void *base, size_t nel, size_t width,
+                        __compar_d_fn_t __compar, void *arg)
+      __attribute__((nonnull (1, 4)));
+
+  #endif
+
+  /* implementation */
+
+  static _SORT_R_INLINE void sort_r(void *base, size_t nel, size_t width,
+                                    int (*compar)(const void *_a,
+                                                  const void *_b, void *_arg),
+                                    void *arg)
+  {
+    #if defined _SORT_R_LINUX
+
+      #if defined __GLIBC__ && ((__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
+
+        /* no qsort_r in glibc before 2.8, need to use nested qsort */
+        sort_r_simple(base, nel, width, compar, arg);
+
+      #else
+
+        qsort_r(base, nel, width, compar, arg);
+
+      #endif
+
+    #elif defined _SORT_R_BSD
+
+      struct sort_r_data tmp;
+      tmp.arg = arg;
+      tmp.compar = compar;
+      qsort_r(base, nel, width, &tmp, sort_r_arg_swap);
+
+    #elif defined _SORT_R_WINDOWS
+
+      struct sort_r_data tmp;
+      tmp.arg = arg;
+      tmp.compar = compar;
+      qsort_s(base, nel, width, sort_r_arg_swap, &tmp);
+
+    #else
+
+      /* Fall back to our own quicksort implementation */
+      sort_r_simple(base, nel, width, compar, arg);
+
+    #endif
+  }
+
+#endif /* !NESTED_QSORT */
+
+#undef _SORT_R_INLINE
+#undef _SORT_R_WINDOWS
+#undef _SORT_R_LINUX
+#undef _SORT_R_BSD
+
+#endif /* SORT_R_H_ */