diff src/fns.c @ 5350:94bbd4792049

Have #'sort*, #'merge use the same test approach as functions from cl-seq.el 2011-02-05 Aidan Kehoe <kehoea@parhasard.net> * fns.c: * fns.c (check_lss_key, check_lss_key_car): New. * fns.c (check_string_lessp_key check_string_lessp_key_car): New. * fns.c (get_merge_predicate): New. * fns.c (list_merge): * fns.c (array_merge): * fns.c (list_array_merge_into_list): * fns.c (list_list_merge_into_array): * fns.c (list_array_merge_into_array): * fns.c (Fmerge): * fns.c (list_sort): * fns.c (array_sort): * fns.c (FsortX): * fns.c (syms_of_fns): * lisp.h: Move #'sort, #'merge to using the same test approach as is used in the functions that take TEST, TEST-NOT and KEY arguments. This allows us to avoid the Ffuncall() overhead when the most common PREDICATE arguments are supplied, in particular #'< and #'string-lessp. * fontcolor-msw.c (sort_font_list_function): * fontcolor-msw.c (mswindows_enumerate_fonts): * dired.c: * dired.c (Fdirectory_files): * fileio.c: * fileio.c (build_annotations): * fileio.c (syms_of_fileio): * keymap.c: * keymap.c (keymap_submaps): * keymap.c (map_keymap_sort_predicate): * keymap.c (describe_map_sort_predicate): * keymap.c (describe_map): Change the various C predicates passed to list_sort () and list_merge () to fit the new calling convention, returning non-zero if the first argument is less than the second, zero otherwise.
author Aidan Kehoe <kehoea@parhasard.net>
date Sat, 05 Feb 2011 12:04:34 +0000
parents 8608eadee6ba
children 70b15ac66ee5 0af042a0c116
line wrap: on
line diff
--- a/src/fns.c	Sun Jan 30 14:27:31 2011 +0100
+++ b/src/fns.c	Sat Feb 05 12:04:34 2011 +0000
@@ -63,7 +63,7 @@
 Lisp_Object Q_test_not, Q_count, Qnsubstitute, Qdelete_duplicates, Qmismatch;
 
 Lisp_Object Qintersection, Qset_difference, Qnset_difference;
-Lisp_Object Qnunion, Qnintersection, Qsubsetp;
+Lisp_Object Qnunion, Qnintersection, Qsubsetp, Qcar_less_than_car;
 
 Lisp_Object Qbase64_conversion_error;
 
@@ -210,9 +210,6 @@
 /* Various test functions for #'member*, #'assoc* and the other functions
    that take both TEST and KEY arguments.  */
 
-typedef Boolint (*check_test_func_t) (Lisp_Object test, Lisp_Object key,
-				      Lisp_Object item, Lisp_Object elt);
-
 static Boolint
 check_eq_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key),
 		Lisp_Object item, Lisp_Object elt)
@@ -439,7 +436,84 @@
 
   return !NILP (elt1);
 }
-
+
+static Boolint
+check_lss_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key),
+		 Lisp_Object elt1, Lisp_Object elt2)
+{
+  return bytecode_arithcompare (elt1, elt2) < 0;
+}
+
+static Boolint
+check_lss_key (Lisp_Object UNUSED (test), Lisp_Object key,
+	       Lisp_Object elt1, Lisp_Object elt2)
+{
+  Lisp_Object args[] = { key, elt1, elt2 };
+  struct gcpro gcpro1;
+
+  GCPRO1 (args[0]);
+  gcpro1.nvars = countof (args);
+  args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args));
+  args[1] = key;
+  args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1));
+  UNGCPRO;
+
+  return bytecode_arithcompare (args[0], args[1]) < 0;
+}
+
+Boolint
+check_lss_key_car (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key),
+		   Lisp_Object elt1, Lisp_Object elt2)
+{
+  struct gcpro gcpro1, gcpro2;
+
+  GCPRO2 (elt1, elt2);
+  elt1 = CONSP (elt1) ? XCAR (elt1) : Fcar (elt1);
+  elt2 = CONSP (elt2) ? XCAR (elt2) : Fcar (elt2);
+  UNGCPRO;
+
+  return bytecode_arithcompare (elt1, elt2) < 0;
+}
+
+Boolint
+check_string_lessp_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key),
+			  Lisp_Object elt1, Lisp_Object elt2)
+{
+  return !NILP (Fstring_lessp (elt1, elt2));
+}
+
+static Boolint
+check_string_lessp_key (Lisp_Object UNUSED (test), Lisp_Object key,
+			Lisp_Object elt1, Lisp_Object elt2)
+{
+  Lisp_Object args[] = { key, elt1, elt2 };
+  struct gcpro gcpro1;
+
+  GCPRO1 (args[0]);
+  gcpro1.nvars = countof (args);
+  args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args));
+  args[1] = key;
+  args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1));
+  UNGCPRO;
+
+  return !NILP (Fstring_lessp (args[0], args[1]));
+}
+
+static Boolint
+check_string_lessp_key_car (Lisp_Object UNUSED (test),
+			    Lisp_Object UNUSED (key),
+			    Lisp_Object elt1, Lisp_Object elt2)
+{
+  struct gcpro gcpro1, gcpro2;
+
+  GCPRO2 (elt1, elt2);
+  elt1 = CONSP (elt1) ? XCAR (elt1) : Fcar (elt1);
+  elt2 = CONSP (elt2) ? XCAR (elt2) : Fcar (elt2);
+  UNGCPRO;
+
+  return !NILP (Fstring_lessp (elt1, elt2));
+}
+
 static check_test_func_t
 get_check_match_function_1 (Lisp_Object item,
 			    Lisp_Object *test_inout, Lisp_Object test_not,
@@ -646,6 +720,72 @@
 				     test_not_unboundp_out, test_func_out);
 }
 
+/* Given PREDICATE and KEY, return a C function pointer appropriate for use
+   in deciding whether one given elements of a sequence is less than
+   another. */
+
+static check_test_func_t
+get_merge_predicate (Lisp_Object predicate, Lisp_Object key)
+{
+  predicate = indirect_function (predicate, 1);
+
+  if (NILP (key))
+    {
+      key = Qidentity;
+    }
+  else
+    {
+      key = indirect_function (key, 1);
+      if (EQ (key, XSYMBOL_FUNCTION (Qidentity)))
+	{
+	  key = Qidentity;
+	}
+    }
+
+  if (EQ (key, Qidentity) && EQ (predicate,
+				 XSYMBOL_FUNCTION (Qcar_less_than_car)))
+    {
+      key = XSYMBOL_FUNCTION (Qcar);
+      predicate = XSYMBOL_FUNCTION (Qlss);
+    }
+
+  if (EQ (predicate, XSYMBOL_FUNCTION (Qlss)))
+    {
+      if (EQ (key, Qidentity))
+	{
+	  return check_lss_nokey;
+	}
+
+      if (EQ (key, XSYMBOL_FUNCTION (Qcar)))
+	{
+	  return check_lss_key_car;
+	}
+
+      return check_lss_key;
+    }
+
+  if (EQ (predicate, XSYMBOL_FUNCTION (Qstring_lessp)))
+    {
+      if (EQ (key, Qidentity))
+	{
+	  return check_string_lessp_nokey;
+	}
+
+      if (EQ (key, XSYMBOL_FUNCTION (Qcar)))
+	{
+	  return check_string_lessp_key_car;
+	}
+
+      return check_string_lessp_key;
+    }
+
+  if (EQ (key, Qidentity))
+    {
+      return check_other_nokey;
+    }
+
+  return check_match_other_key;
+}
 
 DEFUN ("identity", Fidentity, 1, 1, 0, /*
 Return the argument unchanged.
@@ -4694,58 +4834,10 @@
   return result;
 }
 
-static Lisp_Object
-c_merge_predicate_key (Lisp_Object obj1, Lisp_Object obj2,
-                       Lisp_Object pred, Lisp_Object key_func)
-{
-  struct gcpro gcpro1;
-  Lisp_Object args[3];
-
-  /* We could use call2() and call3() here, but we're called O(nlogn) times
-     for a sequence of length n, it make some sense to inline them. */
-  args[0] = key_func;
-  args[1] = obj1;
-  args[2] = Qnil;
-
-  GCPRO1 (args[0]);
-  gcpro1.nvars = countof (args);
-
-  obj1 = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args));
-
-  args[1] = obj2;
-  obj2 = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args));
-
-  args[0] = pred;
-  args[1] = obj1;
-  args[2] = obj2;
-
-  RETURN_UNGCPRO (IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)));
-}
-
-static Lisp_Object
-c_merge_predicate_nokey (Lisp_Object obj1, Lisp_Object obj2,
-                         Lisp_Object pred, Lisp_Object UNUSED (key_func))
-{
-  struct gcpro gcpro1;
-  Lisp_Object args[3];
-
-  /* This is (almost) the implementation of call2, it makes some sense to
-     inline it here. */
-  args[0] = pred;
-  args[1] = obj1;
-  args[2] = obj2;
-
-  GCPRO1 (args[0]);
-  gcpro1.nvars = countof (args);
-
-  RETURN_UNGCPRO (IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)));
-}
-
 Lisp_Object
 list_merge (Lisp_Object org_l1, Lisp_Object org_l2,
-            Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object,
-                                        Lisp_Object, Lisp_Object),
-            Lisp_Object predicate, Lisp_Object key_func)
+	    check_test_func_t check_merge,
+	    Lisp_Object predicate, Lisp_Object key)
 {
   Lisp_Object value;
   Lisp_Object tail;
@@ -4762,15 +4854,8 @@
   tortoises[0] = org_l1;
   tortoises[1] = org_l2; 
 
-  if (NULL == c_predicate)
-    {
-      c_predicate = EQ (key_func, Qidentity) ?
-        c_merge_predicate_nokey : c_merge_predicate_key;
-    }
-
-  /* It is sufficient to protect org_l1 and org_l2.
-     When l1 and l2 are updated, we copy the new values
-     back into the org_ vars.  */
+  /* It is sufficient to protect org_l1 and org_l2.  When l1 and l2 are
+     updated, we copy the new values back into the org_ vars.  */
 
   GCPRO5 (org_l1, org_l2, predicate, value, tortoises[0]);
   gcpro5.nvars = 2;
@@ -4794,7 +4879,7 @@
 	  return value;
 	}
 
-      if (NILP (c_predicate (Fcar (l2), Fcar (l1), predicate, key_func)))
+      if (check_merge (predicate, key, Fcar (l2), Fcar (l1)) == 0)
 	{
 	  tem = l1;
 	  l1 = Fcdr (l1);
@@ -4856,9 +4941,8 @@
 array_merge (Lisp_Object *dest, Elemcount dest_len,
              Lisp_Object *front, Elemcount front_len,
              Lisp_Object *back, Elemcount back_len,
-             Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object,
-                                         Lisp_Object, Lisp_Object),
-             Lisp_Object predicate, Lisp_Object key_func)
+	     check_test_func_t check_merge,
+             Lisp_Object predicate, Lisp_Object key)
 {
   Elemcount ii, fronting, backing;
   Lisp_Object *front_staging = front;
@@ -4920,8 +5004,8 @@
           return;
         }
 
-      if (NILP (c_predicate (back_staging[backing], front_staging[fronting],
-                             predicate, key_func)))
+      if (check_merge (predicate, key, back_staging[backing],
+		       front_staging[fronting]) == 0)
         {
           dest[ii] = front_staging[fronting];
           ++fronting;
@@ -4939,11 +5023,8 @@
 static Lisp_Object
 list_array_merge_into_list (Lisp_Object list,
                             Lisp_Object *array, Elemcount array_len,
-                            Lisp_Object (*c_predicate) (Lisp_Object,
-                                                        Lisp_Object,
-                                                        Lisp_Object,
-                                                        Lisp_Object),
-                            Lisp_Object predicate, Lisp_Object key_func,
+			    check_test_func_t check_merge,
+                            Lisp_Object predicate, Lisp_Object key,
                             Boolint reverse_order)
 {
   Lisp_Object tail = Qnil, value = Qnil, tortoise = list;
@@ -4982,10 +5063,8 @@
 
 
       if (reverse_order ?
-          !NILP (c_predicate (Fcar (list), array [array_index], predicate,
-                              key_func)) :
-          NILP (c_predicate (array [array_index], Fcar (list), predicate,
-                             key_func)))
+	  check_merge (predicate, key, Fcar (list), array [array_index])
+	  : !check_merge (predicate, key, array [array_index], Fcar (list)))
         {
           if (NILP (tail))
             {
@@ -5031,11 +5110,8 @@
 static void
 list_list_merge_into_array (Lisp_Object *output, Elemcount output_len,
                             Lisp_Object list_one, Lisp_Object list_two,
-                            Lisp_Object (*c_predicate) (Lisp_Object,
-                                                        Lisp_Object,
-                                                        Lisp_Object,
-                                                        Lisp_Object),
-                            Lisp_Object predicate, Lisp_Object key_func)
+			    check_test_func_t check_merge,
+                            Lisp_Object predicate, Lisp_Object key)
 {
   Elemcount output_index = 0;
 
@@ -5061,8 +5137,8 @@
           return;
         }
 
-      if (NILP (c_predicate (Fcar (list_two), Fcar (list_one), predicate,
-                             key_func)))
+      if (check_merge (predicate, key, Fcar (list_two), Fcar (list_one))
+	  == 0)
         {
           output [output_index] = XCAR (list_one);
           list_one = XCDR (list_one);
@@ -5083,11 +5159,8 @@
 list_array_merge_into_array (Lisp_Object *output, Elemcount output_len,
                              Lisp_Object list,
                              Lisp_Object *array, Elemcount array_len,
-                             Lisp_Object (*c_predicate) (Lisp_Object,
-                                                         Lisp_Object,
-                                                         Lisp_Object,
-                                                         Lisp_Object),
-                             Lisp_Object predicate, Lisp_Object key_func,
+			     check_test_func_t check_merge,
+                             Lisp_Object predicate, Lisp_Object key,
                              Boolint reverse_order)
 {
   Elemcount output_index = 0, array_index = 0;
@@ -5121,10 +5194,8 @@
         }
 
       if (reverse_order ? 
-          !NILP (c_predicate (Fcar (list), array [array_index], predicate,
-                              key_func)) :
-          NILP (c_predicate (array [array_index], Fcar (list), predicate,
-                             key_func)))
+	  check_merge (predicate, key, Fcar (list), array [array_index]) :
+	  !check_merge (predicate, key, array [array_index], Fcar (list)))
         {
           output [output_index] = XCAR (list);
           list = XCDR (list);
@@ -5172,8 +5243,7 @@
 {
   Lisp_Object type = args[0], sequence_one = args[1], sequence_two = args[2],
     predicate = args[3], result = Qnil;
-  Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object, Lisp_Object,
-			      Lisp_Object);
+  check_test_func_t check_merge = NULL;
 
   PARSE_KEYWORDS (Fmerge, nargs, args, 1, (key), NULL);
 
@@ -5182,8 +5252,7 @@
 
   CHECK_KEY_ARGUMENT (key);
 
-  c_predicate = EQ (key, Qidentity) ?
-    c_merge_predicate_nokey : c_merge_predicate_key;
+  check_merge = get_merge_predicate (predicate, key);
 
   if (EQ (type, Qlist) && (LISTP (sequence_one) || LISTP (sequence_two)))
     {
@@ -5199,7 +5268,7 @@
         }
       else if (CONSP (sequence_one) && CONSP (sequence_two))
 	{
-	  result = list_merge (sequence_one, sequence_two, c_predicate,
+	  result = list_merge (sequence_one, sequence_two, check_merge,
                                predicate, key);
 	}
       else
@@ -5241,8 +5310,7 @@
 
           result = list_array_merge_into_list (sequence_one,
                                                array_storage, array_length,
-                                               c_predicate,
-                                               predicate, key,
+                                               check_merge, predicate, key,
                                                reverse_order);
         }
     }
@@ -5306,8 +5374,7 @@
         {
           list_list_merge_into_array (output + 1, output_len - 1,
                                       sequence_one, sequence_two,
-                                      c_predicate, predicate,
-                                      key);
+                                      check_merge, predicate, key);
         }
       else if (LISTP (sequence_one))
         {
@@ -5315,8 +5382,7 @@
                                        sequence_one,
                                        sequence_two_storage,
                                        sequence_two_len,
-                                       c_predicate, predicate,
-                                       key, 0);
+                                       check_merge, predicate, key, 0);
         }
       else if (LISTP (sequence_two))
         {
@@ -5324,15 +5390,14 @@
                                        sequence_two,
                                        sequence_one_storage,
                                        sequence_one_len,
-                                       c_predicate, predicate,
-                                       key, 1);
+                                       check_merge, predicate, key, 1);
         }
       else
         {
           array_merge (output + 1, output_len - 1,
                        sequence_one_storage, sequence_one_len,
                        sequence_two_storage, sequence_two_len,
-                       c_predicate, predicate,
+                       check_merge, predicate,
                        key);
         }
 
@@ -5349,13 +5414,9 @@
   return result;
 }
 
-/* The sort function should return non-nil if OBJ1 < OBJ2, nil otherwise.
-   NOTE: This is backwards from the way qsort() works. */
 Lisp_Object
-list_sort (Lisp_Object list,
-           Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object, 
-                                       Lisp_Object, Lisp_Object),
-           Lisp_Object predicate, Lisp_Object key_func)
+list_sort (Lisp_Object list, check_test_func_t check_merge,
+	   Lisp_Object predicate, Lisp_Object key)
 {
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
   Lisp_Object back, tem;
@@ -5365,29 +5426,22 @@
   if (XINT (len) < 2)
     return list;
 
-  if (NULL == c_predicate)
-    {
-      c_predicate = EQ (key_func, Qidentity) ? c_merge_predicate_nokey :
-        c_merge_predicate_key;
-    }
-
   len = make_int (XINT (len) / 2 - 1);
   tem = Fnthcdr (len, list);
   back = Fcdr (tem);
   Fsetcdr (tem, Qnil);
 
-  GCPRO4 (front, back, predicate, key_func);
-  front = list_sort (front, c_predicate, predicate, key_func);
-  back = list_sort (back, c_predicate, predicate, key_func);
-
-  RETURN_UNGCPRO (list_merge (front, back, c_predicate, predicate, key_func));
+  GCPRO4 (front, back, predicate, key);
+  front = list_sort (front, check_merge, predicate, key);
+  back = list_sort (back, check_merge, predicate, key);
+
+  RETURN_UNGCPRO (list_merge (front, back, check_merge, predicate, key));
 }
 
 static void
 array_sort (Lisp_Object *array, Elemcount array_len,
-            Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object, 
-                                        Lisp_Object, Lisp_Object),
-            Lisp_Object predicate, Lisp_Object key_func)
+	    check_test_func_t check_merge,
+	    Lisp_Object predicate, Lisp_Object key)
 {
   Elemcount split;
 
@@ -5396,11 +5450,11 @@
 
   split = array_len / 2;
 
-  array_sort (array, split, c_predicate, predicate, key_func);
-  array_sort (array + split, array_len - split, c_predicate, predicate,
-	      key_func);
+  array_sort (array, split, check_merge, predicate, key);
+  array_sort (array + split, array_len - split, check_merge, predicate,
+	      key);
   array_merge (array, array_len, array, split, array + split,
-	       array_len - split, c_predicate, predicate, key_func);
+	       array_len - split, check_merge, predicate, key);
 }            
 
 DEFUN ("sort*", FsortX, 2, MANY, 0, /*
@@ -5423,8 +5477,7 @@
 {
   Lisp_Object sequence = args[0], predicate = args[1];
   Lisp_Object *sequence_carray;
-  Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object, Lisp_Object,
-                              Lisp_Object);
+  check_test_func_t check_merge = NULL;
   Elemcount sequence_len, i;
 
   PARSE_KEYWORDS (FsortX, nargs, args, 1, (key), NULL);
@@ -5433,17 +5486,16 @@
 
   CHECK_KEY_ARGUMENT (key);
 
-  c_predicate = EQ (key, Qidentity) ?
-    c_merge_predicate_nokey : c_merge_predicate_key;
+  check_merge = get_merge_predicate (predicate, key);
 
   if (LISTP (sequence))
     {
-      sequence = list_sort (sequence, c_predicate, predicate, key);
+      sequence = list_sort (sequence, check_merge, predicate, key);
     }
   else if (VECTORP (sequence))
     {
       array_sort (XVECTOR_DATA (sequence), XVECTOR_LENGTH (sequence),
-                  c_predicate, predicate, key);
+                  check_merge, predicate, key);
     }
   else if (STRINGP (sequence))
     {
@@ -5454,7 +5506,7 @@
       STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_carray, i, sequence_len);
 
       /* No GCPRO necessary, characters are immediate. */
-      array_sort (sequence_carray, sequence_len, c_predicate, predicate, key);
+      array_sort (sequence_carray, sequence_len, check_merge, predicate, key);
 
       strdata = XSTRING_DATA (sequence);
 
@@ -5476,7 +5528,7 @@
       BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_carray, i, sequence_len);
 
       /* No GCPRO necessary, bits are immediate. */
-      array_sort (sequence_carray, sequence_len, c_predicate, predicate, key);
+      array_sort (sequence_carray, sequence_len, check_merge, predicate, key);
 
       for (i = 0; i < sequence_len; ++i)
         {
@@ -11698,6 +11750,7 @@
   DEFSYMBOL (Qintersection);
   DEFSYMBOL (Qnintersection);
   DEFSYMBOL (Qsubsetp);
+  DEFSYMBOL (Qcar_less_than_car);
   DEFSYMBOL (Qset_difference);
   DEFSYMBOL (Qnset_difference);
   DEFSYMBOL (Qnunion);