aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2014-08-30 15:59:39 -0700
committerPaul Eggert2014-08-30 15:59:39 -0700
commitf9caea823350640fb03195c73c301f08ce932bd0 (patch)
treebe0e02155cf2f218c61379dde8ac98f100553392 /src
parent88366fcf88e5bccc4d0bcff798beb3ef27aaa496 (diff)
downloademacs-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/ChangeLog14
-rw-r--r--src/fns.c173
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 @@
12014-08-30 Paul Eggert <eggert@cs.ucla.edu> 12014-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.
diff --git a/src/fns.c b/src/fns.c
index f838599230b..57c57884f4d 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -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
50static Lisp_Object Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512; 51static Lisp_Object Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512;
51 52
53static void sort_vector_copy (Lisp_Object, ptrdiff_t,
54 Lisp_Object [restrict], Lisp_Object [restrict]);
52static bool internal_equal (Lisp_Object, Lisp_Object, int, bool, Lisp_Object); 55static bool internal_equal (Lisp_Object, Lisp_Object, int, bool, Lisp_Object);
53 56
54DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0, 57DEFUN ("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 1905static bool
1903static Lisp_Object sort_vector_predicate; 1906inorder (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,
1908static int 1913 except that B might be the last part of DEST. */
1909#ifdef HAVE_QSORT_R 1914static void
1910#if defined (DARWIN_OS) || defined (__FreeBSD__) 1915merge_vectors (Lisp_Object pred,
1911sort_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)],
1913sort_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;
1918sort_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)); 1950static void
1935 less = NILP (call2 (sort_vector_predicate, vq, vp)); 1951sort_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. */
1965static void
1966sort_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
1944static Lisp_Object 1984/* Sort VECTOR in place using PREDICATE, preserving original order of
1985 elements considered as equal. */
1986
1987static void
1945sort_vector (Lisp_Object vector, Lisp_Object predicate) 1988sort_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
1982DEFUN ("sort", Fsort, Ssort, 2, 2, 0, 2008DEFUN ("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);