diff options
| author | Paul Eggert | 2014-08-30 15:59:39 -0700 |
|---|---|---|
| committer | Paul Eggert | 2014-08-30 15:59:39 -0700 |
| commit | f9caea823350640fb03195c73c301f08ce932bd0 (patch) | |
| tree | be0e02155cf2f218c61379dde8ac98f100553392 /src | |
| parent | 88366fcf88e5bccc4d0bcff798beb3ef27aaa496 (diff) | |
| download | emacs-f9caea823350640fb03195c73c301f08ce932bd0.tar.gz emacs-f9caea823350640fb03195c73c301f08ce932bd0.zip | |
Vector-sorting fixes.
It's not safe to call qsort or qsort_r, since they have undefined
behavior if the user-specified predicate is not a total order.
Also, watch out for garbage-collection while sorting vectors.
* admin/merge-gnulib (GNULIB_MODULES): Add vla.
* configure.ac (qsort_r): Remove, as we no longer use qsort-like
functions.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* lib/vla.h, m4/vararrays.m4: New files, copied from gnulib.
* lib/stdlib.in.h, m4/stdlib_h.m4: Sync from gnulib, incorporating:
2014-08-29 qsort_r: new module, for GNU-style qsort_r
The previous two files' changes are boilerplate generated by
admin/merge-gnulib, and should not affect Emacs.
* src/fns.c: Include <vla.h>.
(sort_vector_predicate) [!HAVE_QSORT_R]: Remove.
(sort_vector_compare): Remove, replacing with ....
(inorder, merge_vectors, sort_vector_inplace, sort_vector_copy):
... these new functions.
(sort_vector): Rewrite to use the new functions.
GCPRO locals, since the predicate can invoke the GC.
Since it's in-place return void; caller changed.
(merge): Use 'inorder', for clarity.
Fixes: debbugs:18361
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 14 | ||||
| -rw-r--r-- | src/fns.c | 173 |
2 files changed, 113 insertions, 74 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index b348932f0a9..00ec5dcf3d6 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,5 +1,19 @@ | |||
| 1 | 2014-08-30 Paul Eggert <eggert@cs.ucla.edu> | 1 | 2014-08-30 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 2 | ||
| 3 | Vector-sorting fixes (Bug#18361). | ||
| 4 | It's not safe to call qsort or qsort_r, since they have undefined | ||
| 5 | behavior if the user-specified predicate is not a total order. | ||
| 6 | Also, watch out for garbage-collection while sorting vectors. | ||
| 7 | * fns.c: Include <vla.h>. | ||
| 8 | (sort_vector_predicate) [!HAVE_QSORT_R]: Remove. | ||
| 9 | (sort_vector_compare): Remove, replacing with .... | ||
| 10 | (inorder, merge_vectors, sort_vector_inplace, sort_vector_copy): | ||
| 11 | ... these new functions. | ||
| 12 | (sort_vector): Rewrite to use the new functions. | ||
| 13 | GCPRO locals, since the predicate can invoke the GC. | ||
| 14 | Since it's in-place return void; caller changed. | ||
| 15 | (merge): Use 'inorder', for clarity. | ||
| 16 | |||
| 3 | * sysdep.c (str_collate): Clear errno just before wcscoll(_l). | 17 | * sysdep.c (str_collate): Clear errno just before wcscoll(_l). |
| 4 | One can't hoist this out of the 'if', because intervening calls to | 18 | One can't hoist this out of the 'if', because intervening calls to |
| 5 | newlocale, twolower, etc. can change errno. | 19 | newlocale, twolower, etc. can change errno. |
| @@ -24,6 +24,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 24 | #include <time.h> | 24 | #include <time.h> |
| 25 | 25 | ||
| 26 | #include <intprops.h> | 26 | #include <intprops.h> |
| 27 | #include <vla.h> | ||
| 27 | 28 | ||
| 28 | #include "lisp.h" | 29 | #include "lisp.h" |
| 29 | #include "commands.h" | 30 | #include "commands.h" |
| @@ -49,6 +50,8 @@ static Lisp_Object Qcodeset, Qdays, Qmonths, Qpaper; | |||
| 49 | 50 | ||
| 50 | static Lisp_Object Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512; | 51 | static Lisp_Object Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512; |
| 51 | 52 | ||
| 53 | static void sort_vector_copy (Lisp_Object, ptrdiff_t, | ||
| 54 | Lisp_Object [restrict], Lisp_Object [restrict]); | ||
| 52 | static bool internal_equal (Lisp_Object, Lisp_Object, int, bool, Lisp_Object); | 55 | static bool internal_equal (Lisp_Object, Lisp_Object, int, bool, Lisp_Object); |
| 53 | 56 | ||
| 54 | DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0, | 57 | DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0, |
| @@ -1897,86 +1900,109 @@ sort_list (Lisp_Object list, Lisp_Object predicate) | |||
| 1897 | return merge (front, back, predicate); | 1900 | return merge (front, back, predicate); |
| 1898 | } | 1901 | } |
| 1899 | 1902 | ||
| 1900 | /* Using GNU qsort_r, we can pass this as a parameter. This also | 1903 | /* Using PRED to compare, return whether A and B are in order. |
| 1901 | exists on FreeBSD and Darwin/OSX, but with a different signature. */ | 1904 | Compare stably when A appeared before B in the input. */ |
| 1902 | #ifndef HAVE_QSORT_R | 1905 | static bool |
| 1903 | static Lisp_Object sort_vector_predicate; | 1906 | inorder (Lisp_Object pred, Lisp_Object a, Lisp_Object b) |
| 1904 | #endif | 1907 | { |
| 1908 | return NILP (call2 (pred, b, a)); | ||
| 1909 | } | ||
| 1905 | 1910 | ||
| 1906 | /* Comparison function called by qsort. */ | 1911 | /* Using PRED to compare, merge from ALEN-length A and BLEN-length B |
| 1907 | 1912 | into DEST. Argument arrays must be nonempty and must not overlap, | |
| 1908 | static int | 1913 | except that B might be the last part of DEST. */ |
| 1909 | #ifdef HAVE_QSORT_R | 1914 | static void |
| 1910 | #if defined (DARWIN_OS) || defined (__FreeBSD__) | 1915 | merge_vectors (Lisp_Object pred, |
| 1911 | sort_vector_compare (void *arg, const void *p, const void *q) | 1916 | ptrdiff_t alen, Lisp_Object const a[restrict VLA_ELEMS (alen)], |
| 1912 | #elif defined (GNU_LINUX) | 1917 | ptrdiff_t blen, Lisp_Object const b[VLA_ELEMS (blen)], |
| 1913 | sort_vector_compare (const void *p, const void *q, void *arg) | 1918 | Lisp_Object dest[VLA_ELEMS (alen + blen)]) |
| 1914 | #else /* neither darwin/bsd nor gnu/linux */ | 1919 | { |
| 1915 | #error "check how qsort_r comparison function works on your platform" | 1920 | eassume (0 < alen && 0 < blen); |
| 1916 | #endif /* DARWIN_OS || __FreeBSD__ */ | 1921 | Lisp_Object const *alim = a + alen; |
| 1917 | #else /* not HAVE_QSORT_R */ | 1922 | Lisp_Object const *blim = b + blen; |
| 1918 | sort_vector_compare (const void *p, const void *q) | ||
| 1919 | #endif /* HAVE_QSORT_R */ | ||
| 1920 | { | ||
| 1921 | bool more, less; | ||
| 1922 | Lisp_Object op, oq, vp, vq; | ||
| 1923 | #ifdef HAVE_QSORT_R | ||
| 1924 | Lisp_Object sort_vector_predicate = *(Lisp_Object *) arg; | ||
| 1925 | #endif | ||
| 1926 | 1923 | ||
| 1927 | op = *(Lisp_Object *) p; | 1924 | while (true) |
| 1928 | oq = *(Lisp_Object *) q; | 1925 | { |
| 1929 | vp = XSAVE_OBJECT (op, 1); | 1926 | if (inorder (pred, a[0], b[0])) |
| 1930 | vq = XSAVE_OBJECT (oq, 1); | 1927 | { |
| 1928 | *dest++ = *a++; | ||
| 1929 | if (a == alim) | ||
| 1930 | { | ||
| 1931 | if (dest != b) | ||
| 1932 | memcpy (dest, b, (blim - b) * sizeof *dest); | ||
| 1933 | return; | ||
| 1934 | } | ||
| 1935 | } | ||
| 1936 | else | ||
| 1937 | { | ||
| 1938 | *dest++ = *b++; | ||
| 1939 | if (b == blim) | ||
| 1940 | { | ||
| 1941 | memcpy (dest, a, (alim - a) * sizeof *dest); | ||
| 1942 | return; | ||
| 1943 | } | ||
| 1944 | } | ||
| 1945 | } | ||
| 1946 | } | ||
| 1931 | 1947 | ||
| 1932 | /* Use recorded element index as a secondary key to | 1948 | /* Using PRED to compare, sort LEN-length VEC in place, using TMP for |
| 1933 | preserve original order. Pretty ugly but works. */ | 1949 | temporary storage. LEN must be at least 2. */ |
| 1934 | more = NILP (call2 (sort_vector_predicate, vp, vq)); | 1950 | static void |
| 1935 | less = NILP (call2 (sort_vector_predicate, vq, vp)); | 1951 | sort_vector_inplace (Lisp_Object pred, ptrdiff_t len, |
| 1936 | return ((more && !less) ? 1 | 1952 | Lisp_Object vec[restrict VLA_ELEMS (len)], |
| 1937 | : ((!more && less) ? -1 | 1953 | Lisp_Object tmp[restrict VLA_ELEMS (len >> 1)]) |
| 1938 | : XSAVE_INTEGER (op, 0) - XSAVE_INTEGER (oq, 0))); | 1954 | { |
| 1955 | eassume (2 <= len); | ||
| 1956 | ptrdiff_t halflen = len >> 1; | ||
| 1957 | sort_vector_copy (pred, halflen, vec, tmp); | ||
| 1958 | if (1 < len - halflen) | ||
| 1959 | sort_vector_inplace (pred, len - halflen, vec + halflen, vec); | ||
| 1960 | merge_vectors (pred, halflen, tmp, len - halflen, vec + halflen, vec); | ||
| 1939 | } | 1961 | } |
| 1940 | 1962 | ||
| 1941 | /* Sort VECTOR using PREDICATE, preserving original order of elements | 1963 | /* Using PRED to compare, sort from LEN-length SRC into DST. |
| 1942 | considered as equal. */ | 1964 | Len must be positive. */ |
| 1965 | static void | ||
| 1966 | sort_vector_copy (Lisp_Object pred, ptrdiff_t len, | ||
| 1967 | Lisp_Object src[restrict VLA_ELEMS (len)], | ||
| 1968 | Lisp_Object dest[restrict VLA_ELEMS (len)]) | ||
| 1969 | { | ||
| 1970 | eassume (0 < len); | ||
| 1971 | ptrdiff_t halflen = len >> 1; | ||
| 1972 | if (halflen < 1) | ||
| 1973 | dest[0] = src[0]; | ||
| 1974 | else | ||
| 1975 | { | ||
| 1976 | if (1 < halflen) | ||
| 1977 | sort_vector_inplace (pred, halflen, src, dest); | ||
| 1978 | if (1 < len - halflen) | ||
| 1979 | sort_vector_inplace (pred, len - halflen, src + halflen, dest); | ||
| 1980 | merge_vectors (pred, halflen, src, len - halflen, src + halflen, dest); | ||
| 1981 | } | ||
| 1982 | } | ||
| 1943 | 1983 | ||
| 1944 | static Lisp_Object | 1984 | /* Sort VECTOR in place using PREDICATE, preserving original order of |
| 1985 | elements considered as equal. */ | ||
| 1986 | |||
| 1987 | static void | ||
| 1945 | sort_vector (Lisp_Object vector, Lisp_Object predicate) | 1988 | sort_vector (Lisp_Object vector, Lisp_Object predicate) |
| 1946 | { | 1989 | { |
| 1947 | ptrdiff_t i; | 1990 | ptrdiff_t len = ASIZE (vector); |
| 1948 | EMACS_INT len = ASIZE (vector); | ||
| 1949 | Lisp_Object *v = XVECTOR (vector)->contents; | ||
| 1950 | |||
| 1951 | if (len < 2) | 1991 | if (len < 2) |
| 1952 | return vector; | 1992 | return; |
| 1953 | /* Record original index of each element to make qsort stable. */ | 1993 | ptrdiff_t halflen = len >> 1; |
| 1954 | for (i = 0; i < len; i++) | 1994 | Lisp_Object *tmp; |
| 1955 | v[i] = make_save_int_obj (i, v[i]); | 1995 | struct gcpro gcpro1, gcpro2, gcpro3; |
| 1956 | 1996 | GCPRO3 (vector, predicate, predicate); | |
| 1957 | /* Setup predicate and sort. */ | 1997 | USE_SAFE_ALLOCA; |
| 1958 | #ifdef HAVE_QSORT_R | 1998 | SAFE_ALLOCA_LISP (tmp, halflen); |
| 1959 | #if defined (DARWIN_OS) || defined (__FreeBSD__) | 1999 | for (ptrdiff_t i = 0; i < halflen; i++) |
| 1960 | qsort_r (v, len, word_size, (void *) &predicate, sort_vector_compare); | 2000 | tmp[i] = make_number (0); |
| 1961 | #elif defined (GNU_LINUX) | 2001 | gcpro3.var = tmp; |
| 1962 | qsort_r (v, len, word_size, sort_vector_compare, (void *) &predicate); | 2002 | gcpro3.nvars = halflen; |
| 1963 | #else /* neither darwin/bsd nor gnu/linux */ | 2003 | sort_vector_inplace (predicate, len, XVECTOR (vector)->contents, tmp); |
| 1964 | #error "check how qsort_r works on your platform" | 2004 | UNGCPRO; |
| 1965 | #endif /* DARWIN_OS || __FreeBSD__ */ | 2005 | SAFE_FREE (); |
| 1966 | #else /* not HAVE_QSORT_R */ | ||
| 1967 | sort_vector_predicate = predicate; | ||
| 1968 | qsort (v, len, word_size, sort_vector_compare); | ||
| 1969 | #endif /* HAVE_QSORT_R */ | ||
| 1970 | |||
| 1971 | /* Discard indexes and restore original elements. */ | ||
| 1972 | for (i = 0; i < len; i++) | ||
| 1973 | { | ||
| 1974 | Lisp_Object save = v[i]; | ||
| 1975 | /* Use explicit free to offload GC. */ | ||
| 1976 | v[i] = XSAVE_OBJECT (save, 1); | ||
| 1977 | free_misc (save); | ||
| 1978 | } | ||
| 1979 | return vector; | ||
| 1980 | } | 2006 | } |
| 1981 | 2007 | ||
| 1982 | DEFUN ("sort", Fsort, Ssort, 2, 2, 0, | 2008 | DEFUN ("sort", Fsort, Ssort, 2, 2, 0, |
| @@ -1990,7 +2016,7 @@ if the first element should sort before the second. */) | |||
| 1990 | if (CONSP (seq)) | 2016 | if (CONSP (seq)) |
| 1991 | seq = sort_list (seq, predicate); | 2017 | seq = sort_list (seq, predicate); |
| 1992 | else if (VECTORP (seq)) | 2018 | else if (VECTORP (seq)) |
| 1993 | seq = sort_vector (seq, predicate); | 2019 | sort_vector (seq, predicate); |
| 1994 | else if (!NILP (seq)) | 2020 | else if (!NILP (seq)) |
| 1995 | wrong_type_argument (Qsequencep, seq); | 2021 | wrong_type_argument (Qsequencep, seq); |
| 1996 | return seq; | 2022 | return seq; |
| @@ -2033,8 +2059,7 @@ merge (Lisp_Object org_l1, Lisp_Object org_l2, Lisp_Object pred) | |||
| 2033 | Fsetcdr (tail, l1); | 2059 | Fsetcdr (tail, l1); |
| 2034 | return value; | 2060 | return value; |
| 2035 | } | 2061 | } |
| 2036 | tem = call2 (pred, Fcar (l2), Fcar (l1)); | 2062 | if (inorder (pred, Fcar (l1), Fcar (l2))) |
| 2037 | if (NILP (tem)) | ||
| 2038 | { | 2063 | { |
| 2039 | tem = l1; | 2064 | tem = l1; |
| 2040 | l1 = Fcdr (l1); | 2065 | l1 = Fcdr (l1); |