Mercurial > hg > xemacs-beta
diff src/fns.c @ 5182:2e528066e2fc
Move #'sort*, #'fill, #'merge to C from cl-seq.el.
lisp/ChangeLog addition:
2010-04-01 Aidan Kehoe <kehoea@parhasard.net>
* cl-seq.el (fill, sort*, merge): Move these functions to fns.c.
(stable-sort): Make this docstring reflect the argument names used
in the #'sort* docstring.
* cl-macs.el (stable-sort): Make #'stable-sort exactly equivalent
to #'sort* in compiled code.
* bytecomp.el (byte-compile-maybe-add-*):
New macro, for functions like #'sort and #'mapcar that, to be
strictly compatible, should only take two args, but in our
implementation can take more, because they're aliases of #'sort*
and #'mapcar*.
(byte-compile-mapcar, byte-compile-sort, byte-compile-fillarray):
Use this new macro.
(map-into): Add a byte-compile method for #'map-into in passing.
* apropos.el (apropos-print): Use #'sort* with a :key argument,
now it's in C.
* compat.el (extent-at): Ditto.
* register.el (list-registers): Ditto.
* package-ui.el (pui-list-packages): Ditto.
* help.el (sorted-key-descriptions): Ditto.
src/ChangeLog addition:
2010-03-31 Aidan Kehoe <kehoea@parhasard.net>
* fns.c (STRING_DATA_TO_OBJECT_ARRAY)
(BIT_VECTOR_TO_OBJECT_ARRAY, c_merge_predicate_key)
(c_merge_predicate_nokey, list_merge, array_merge)
(list_array_merge_into_list, list_list_merge_into_array)
(list_array_merge_into_array, CHECK_KEY_ARGUMENT, Fmerge)
(list_sort, array_sort, FsortX):
Move #'sort*, #'fill, #'merge from cl-seq.el to C, extending the
implementations of Fsort, Ffillarray, and merge() to do so.
* keymap.c (keymap_submaps, map_keymap_sort_predicate)
(describe_map_sort_predicate):
Change the calling semantics of the C sort predicates to return a
non-nil Lisp object if the first argument is less than the second,
rather than C integers.
* fontcolor-msw.c (sort_font_list_function):
* fileio.c (build_annotations):
* dired.c (Fdirectory_files):
* abbrev.c (Finsert_abbrev_table_description):
Call list_sort instead of Fsort, list_merge instead of merge() in
these functions.
man/ChangeLog addition:
2010-04-01 Aidan Kehoe <kehoea@parhasard.net>
* lispref/lists.texi (Rearrangement):
Update the documentation of #'sort here, now that it accepts any
type of sequence and the KEY keyword argument. (Though this is
probably now the wrong place for this function, given that.)
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Thu, 01 Apr 2010 20:22:50 +0100 |
parents | 7be849cb8828 |
children | 039d9a7f2e6d |
line wrap: on
line diff
--- a/src/fns.c Mon Mar 29 23:23:33 2010 -0500 +++ b/src/fns.c Thu Apr 01 20:22:50 2010 +0100 @@ -54,9 +54,9 @@ /* NOTE: This symbol is also used in lread.c */ #define FEATUREP_SYNTAX -Lisp_Object Qstring_lessp; +Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill; Lisp_Object Qidentity; -Lisp_Object Qvector, Qarray, Qbit_vector; +Lisp_Object Qvector, Qarray, Qbit_vector, QsortX; Lisp_Object Qbase64_conversion_error; @@ -1936,100 +1936,82 @@ return reversed_list; } -static Lisp_Object list_merge (Lisp_Object org_l1, Lisp_Object org_l2, - Lisp_Object lisp_arg, - int (*pred_fn) (Lisp_Object, Lisp_Object, - Lisp_Object lisp_arg)); - -/* The sort function should return > 0 if OBJ1 < OBJ2, < 0 otherwise. - NOTE: This is backwards from the way qsort() works. */ - -Lisp_Object -list_sort (Lisp_Object list, - Lisp_Object lisp_arg, - int (*pred_fn) (Lisp_Object obj1, Lisp_Object obj2, - Lisp_Object lisp_arg)) +static Lisp_Object +c_merge_predicate_key (Lisp_Object obj1, Lisp_Object obj2, + Lisp_Object pred, Lisp_Object key_func) { - struct gcpro gcpro1, gcpro2, gcpro3; - Lisp_Object back, tem; - Lisp_Object front = list; - Lisp_Object len = Flength (list); - - if (XINT (len) < 2) - return list; - - len = make_int (XINT (len) / 2 - 1); - tem = Fnthcdr (len, list); - back = Fcdr (tem); - Fsetcdr (tem, Qnil); - - GCPRO3 (front, back, lisp_arg); - front = list_sort (front, lisp_arg, pred_fn); - back = list_sort (back, lisp_arg, pred_fn); - UNGCPRO; - return list_merge (front, back, lisp_arg, pred_fn); + 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 int -merge_pred_function (Lisp_Object obj1, Lisp_Object obj2, - Lisp_Object pred) +static Lisp_Object +c_merge_predicate_nokey (Lisp_Object obj1, Lisp_Object obj2, + Lisp_Object pred, Lisp_Object UNUSED (key_func)) { - Lisp_Object tmp; - - /* prevents the GC from happening in call2 */ - /* Emacs' GC doesn't actually relocate pointers, so this probably - isn't strictly necessary */ - int speccount = begin_gc_forbidden (); - tmp = call2 (pred, obj1, obj2); - unbind_to (speccount); - - if (NILP (tmp)) - return -1; - else - return 1; -} - -DEFUN ("sort", Fsort, 2, 2, 0, /* -Sort LIST, stably, comparing elements using PREDICATE. -Returns the sorted list. LIST is modified by side effects. -PREDICATE is called with two elements of LIST, and should return T -if the first element is "less" than the second. -*/ - (list, predicate)) -{ - return list_sort (list, predicate, merge_pred_function); + 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 -merge (Lisp_Object org_l1, Lisp_Object org_l2, - Lisp_Object pred) -{ - return list_merge (org_l1, org_l2, pred, merge_pred_function); -} - - -static Lisp_Object list_merge (Lisp_Object org_l1, Lisp_Object org_l2, - Lisp_Object lisp_arg, - int (*pred_fn) (Lisp_Object, Lisp_Object, Lisp_Object lisp_arg)) + Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object), + Lisp_Object predicate, Lisp_Object key_func) { Lisp_Object value; Lisp_Object tail; Lisp_Object tem; Lisp_Object l1, l2; struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + int looped = 0; l1 = org_l1; l2 = org_l2; tail = Qnil; value = Qnil; + 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. */ - GCPRO4 (org_l1, org_l2, lisp_arg, value); + GCPRO4 (org_l1, org_l2, predicate, value); while (1) { @@ -2050,7 +2032,7 @@ return value; } - if (((*pred_fn) (Fcar (l2), Fcar (l1), lisp_arg)) < 0) + if (NILP (c_predicate (Fcar (l2), Fcar (l1), predicate, key_func))) { tem = l1; l1 = Fcdr (l1); @@ -2067,9 +2049,682 @@ else Fsetcdr (tail, tem); tail = tem; + + if (++looped % CIRCULAR_LIST_SUSPICION_LENGTH) continue; + + /* Just check the lists aren't circular:*/ + { + EXTERNAL_LIST_LOOP_1 (l1) + { + } + } + { + EXTERNAL_LIST_LOOP_1 (l2) + { + } + } + } +} + +static void +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) +{ + Elemcount ii, fronting, backing; + Lisp_Object *front_staging = front; + Lisp_Object *back_staging = back; + struct gcpro gcpro1, gcpro2; + + assert (dest_len == (back_len + front_len)); + + if (0 == dest_len) + { + return; + } + + if (front >= dest && front < (dest + dest_len)) + { + front_staging = alloca_array (Lisp_Object, front_len); + + for (ii = 0; ii < front_len; ++ii) + { + front_staging[ii] = front[ii]; + } + } + + if (back >= dest && back < (dest + dest_len)) + { + back_staging = alloca_array (Lisp_Object, back_len); + + for (ii = 0; ii < back_len; ++ii) + { + back_staging[ii] = back[ii]; + } + } + + GCPRO2 (front_staging[0], back_staging[0]); + gcpro1.nvars = front_len; + gcpro2.nvars = back_len; + + for (ii = fronting = backing = 0; ii < dest_len; ++ii) + { + if (fronting >= front_len) + { + while (ii < dest_len) + { + dest[ii] = back_staging[backing]; + ++ii, ++backing; + } + UNGCPRO; + return; + } + + if (backing >= back_len) + { + while (ii < dest_len) + { + dest[ii] = front_staging[fronting]; + ++ii, ++fronting; + } + UNGCPRO; + return; + } + + if (NILP (c_predicate (back_staging[backing], front_staging[fronting], + predicate, key_func))) + { + dest[ii] = front_staging[fronting]; + ++fronting; + } + else + { + dest[ii] = back_staging[backing]; + ++backing; + } + } + + UNGCPRO; +} + +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, + Boolint reverse_order) +{ + Lisp_Object tail = Qnil, value = Qnil; + struct gcpro gcpro1, gcpro2, gcpro3; + Elemcount array_index = 0; + int looped = 0; + + GCPRO3 (list, tail, value); + + while (1) + { + if (NILP (list)) + { + UNGCPRO; + + if (NILP (tail)) + { + return Flist (array_len, array); + } + + Fsetcdr (tail, Flist (array_len - array_index, array + array_index)); + return value; + } + + if (array_index >= array_len) + { + UNGCPRO; + if (NILP (tail)) + { + return list; + } + + Fsetcdr (tail, list); + return value; + } + + + 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))) + { + if (NILP (tail)) + { + value = tail = list; + } + else + { + Fsetcdr (tail, list); + tail = XCDR (tail); + } + + list = Fcdr (list); + } + else + { + if (NILP (tail)) + { + value = tail = Fcons (array [array_index], Qnil); + } + else + { + Fsetcdr (tail, Fcons (array [array_index], tail)); + tail = XCDR (tail); + } + ++array_index; + } + + if (++looped % CIRCULAR_LIST_SUSPICION_LENGTH) continue; + + { + EXTERNAL_LIST_LOOP_1 (list) + { + } + } + } +} + +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) +{ + Elemcount output_index = 0; + + while (output_index < output_len) + { + if (NILP (list_one)) + { + while (output_index < output_len) + { + output [output_index] = Fcar (list_two); + list_two = Fcdr (list_two), ++output_index; + } + return; + } + + if (NILP (list_two)) + { + while (output_index < output_len) + { + output [output_index] = Fcar (list_one); + list_one = Fcdr (list_one), ++output_index; + } + return; + } + + if (NILP (c_predicate (Fcar (list_two), Fcar (list_one), predicate, + key_func))) + { + output [output_index] = XCAR (list_one); + list_one = XCDR (list_one); + } + else + { + output [output_index] = XCAR (list_two); + list_two = XCDR (list_two); + } + + ++output_index; + + /* No need to check for circularity. */ + } +} + +static void +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, + Boolint reverse_order) +{ + Elemcount output_index = 0, array_index = 0; + + while (output_index < output_len) + { + if (NILP (list)) + { + if (array_len - array_index != output_len - output_index) + { + invalid_state ("List length modified during merge", Qunbound); + } + + while (array_index < array_len) + { + output [output_index++] = array [array_index++]; + } + + return; + } + + if (array_index >= array_len) + { + while (output_index < output_len) + { + output [output_index++] = Fcar (list); + list = Fcdr (list); + } + + return; + } + + 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))) + { + output [output_index] = XCAR (list); + list = XCDR (list); + } + else + { + output [output_index] = array [array_index]; + ++array_index; + } + + ++output_index; } } +#define STRING_DATA_TO_OBJECT_ARRAY(strdata, c_array, counter, len) \ + do { \ + c_array = alloca_array (Lisp_Object, len); \ + for (counter = 0; counter < len; ++counter) \ + { \ + c_array[counter] = make_char (itext_ichar (strdata)); \ + INC_IBYTEPTR (strdata); \ + } \ + } while (0) + +#define BIT_VECTOR_TO_OBJECT_ARRAY(v, c_array, counter, len) do { \ + c_array = alloca_array (Lisp_Object, len); \ + for (counter = 0; counter < len; ++counter) \ + { \ + c_array[counter] = make_int (bit_vector_bit (v, counter)); \ + } \ + } while (0) + +/* This macro might eventually find a better home than here. */ + +#define CHECK_KEY_ARGUMENT(key, c_predicate) \ + do { \ + if (NILP (key)) \ + { \ + key = Qidentity; \ + } \ + \ + if (EQ (key, Qidentity)) \ + { \ + c_predicate = c_merge_predicate_nokey; \ + } \ + else \ + { \ + key = indirect_function (key, 1); \ + c_predicate = c_merge_predicate_key; \ + } \ + } while (0) + +DEFUN ("merge", Fmerge, 4, MANY, 0, /* +Destructively merge SEQUENCE-ONE and SEQUENCE-TWO, producing a new sequence. + +TYPE is the type of sequence to return. PREDICATE is a `less-than' +predicate on the elements. + +Optional keyword argument KEY is a function used to extract an object to be +used for comparison from each element of SEQUENCE-ONE and SEQUENCE-TWO. + +arguments: (TYPE SEQUENCE-ONE SEQUENCE-TWO PREDICATE &key (KEY #'IDENTITY)) +*/ + (int nargs, Lisp_Object *args)) +{ + 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); + + PARSE_KEYWORDS (Qmerge, nargs, args, 4, 1, (key), NULL, 0); + + CHECK_SEQUENCE (sequence_one); + CHECK_SEQUENCE (sequence_two); + + CHECK_KEY_ARGUMENT (key, c_predicate); + + if (EQ (type, Qlist) && (LISTP (sequence_one) || LISTP (sequence_two))) + { + if (NILP (sequence_two)) + { + result = Fappend (2, args + 1); + } + else if (NILP (sequence_one)) + { + args[3] = Qnil; /* Overwriting PREDICATE, and losing its GC + protection, but that doesn't matter. */ + result = Fappend (2, args + 2); + } + else if (CONSP (sequence_one) && CONSP (sequence_two)) + { + result = list_merge (sequence_one, sequence_two, c_predicate, + predicate, key); + } + else + { + Lisp_Object *array_storage, swap; + Elemcount array_length, i; + Boolint reverse_order = 0; + + if (!CONSP (sequence_one)) + { + /* Make sequence_one the cons, sequence_two the array: */ + swap = sequence_one; + sequence_one = sequence_two; + sequence_two = swap; + reverse_order = 1; + } + + if (VECTORP (sequence_two)) + { + array_storage = XVECTOR_DATA (sequence_two); + array_length = XVECTOR_LENGTH (sequence_two); + } + else if (STRINGP (sequence_two)) + { + Ibyte *strdata = XSTRING_DATA (sequence_two); + array_length = string_char_length (sequence_two); + /* No need to GCPRO, characters are immediate. */ + STRING_DATA_TO_OBJECT_ARRAY (strdata, array_storage, i, + array_length); + + } + else + { + Lisp_Bit_Vector *v = XBIT_VECTOR (sequence_two); + array_length = bit_vector_length (v); + /* No need to GCPRO, fixnums are immediate. */ + BIT_VECTOR_TO_OBJECT_ARRAY (v, array_storage, i, array_length); + } + + result = list_array_merge_into_list (sequence_one, + array_storage, array_length, + c_predicate, + predicate, key, + reverse_order); + } + } + else + { + Elemcount sequence_one_len = XINT (Flength (sequence_one)), + sequence_two_len = XINT (Flength (sequence_two)), i; + Elemcount output_len = 1 + sequence_one_len + sequence_two_len; + Lisp_Object *output = alloca_array (Lisp_Object, output_len), + *sequence_one_storage = NULL, *sequence_two_storage = NULL; + Boolint do_coerce = !(EQ (type, Qvector) || EQ (type, Qstring) + || EQ (type, Qbit_vector) || EQ (type, Qlist)); + Ibyte *strdata = NULL; + Lisp_Bit_Vector *v = NULL; + struct gcpro gcpro1; + + output[0] = do_coerce ? Qlist : type; + for (i = 1; i < output_len; ++i) + { + output[i] = Qnil; + } + + GCPRO1 (output[0]); + gcpro1.nvars = output_len; + + if (VECTORP (sequence_one)) + { + sequence_one_storage = XVECTOR_DATA (sequence_one); + } + else if (STRINGP (sequence_one)) + { + strdata = XSTRING_DATA (sequence_one); + STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_one_storage, + i, sequence_one_len); + } + else if (BIT_VECTORP (sequence_one)) + { + v = XBIT_VECTOR (sequence_one); + BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_one_storage, + i, sequence_one_len); + } + + if (VECTORP (sequence_two)) + { + sequence_two_storage = XVECTOR_DATA (sequence_two); + } + else if (STRINGP (sequence_two)) + { + strdata = XSTRING_DATA (sequence_two); + STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_two_storage, + i, sequence_two_len); + } + else if (BIT_VECTORP (sequence_two)) + { + v = XBIT_VECTOR (sequence_two); + BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_two_storage, + i, sequence_two_len); + } + + if (LISTP (sequence_one) && LISTP (sequence_two)) + { + list_list_merge_into_array (output + 1, output_len - 1, + sequence_one, sequence_two, + c_predicate, predicate, + key); + } + else if (LISTP (sequence_one)) + { + list_array_merge_into_array (output + 1, output_len - 1, + sequence_one, + sequence_two_storage, + sequence_two_len, + c_predicate, predicate, + key, 0); + } + else if (LISTP (sequence_two)) + { + list_array_merge_into_array (output + 1, output_len - 1, + sequence_two, + sequence_one_storage, + sequence_one_len, + c_predicate, 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, + key); + } + + result = Ffuncall (output_len, output); + + if (do_coerce) + { + result = call2 (Qcoerce, result, type); + } + + UNGCPRO; + } + + 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) +{ + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + Lisp_Object back, tem; + Lisp_Object front = list; + Lisp_Object len = Flength (list); + + 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)); +} + +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) +{ + Elemcount split; + + if (array_len < 2) + return; + + 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_merge (array, array_len, array, split, array + split, + array_len - split, c_predicate, predicate, key_func); +} + +DEFUN ("sort*", FsortX, 2, MANY, 0, /* +Sort SEQUENCE, comparing elements using PREDICATE. +Returns the sorted sequence. SEQUENCE is modified by side effect. + +PREDICATE is called with two elements of SEQUENCE, and should return t if +the first element is `less' than the second. + +Optional keyword argument KEY is a function used to extract an object to be +used for comparison from each element of SEQUENCE. + +In this implementation, sorting is always stable; but call `stable-sort' if +this stability is important to you, other implementations may not make the +same guarantees. + +arguments: (SEQUENCE PREDICATE &key (KEY #'IDENTITY)) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence = args[0], predicate = args[1]; + Lisp_Object *sequence_carray; + Lisp_Object (*c_predicate) (Lisp_Object, Lisp_Object, Lisp_Object, + Lisp_Object); + Elemcount sequence_len, i; + + PARSE_KEYWORDS (QsortX, nargs, args, 2, 1, (key), NULL, 0); + + CHECK_SEQUENCE (sequence); + + CHECK_KEY_ARGUMENT (key, c_predicate); + + if (LISTP (sequence)) + { + sequence = list_sort (sequence, c_predicate, predicate, key); + } + else if (VECTORP (sequence)) + { + array_sort (XVECTOR_DATA (sequence), XVECTOR_LENGTH (sequence), + c_predicate, predicate, key); + } + else if (STRINGP (sequence)) + { + Ibyte *strdata = XSTRING_DATA (sequence); + Elemcount string_ascii_begin = 0; + Ichar ch; + + sequence_len = string_char_length (sequence); + + 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); + + strdata = XSTRING_DATA (sequence); + + CHECK_LISP_WRITEABLE (sequence); + for (i = 0; i < sequence_len; ++i) + { + ch = XCHAR (sequence_carray[i]); + strdata += set_itext_ichar (strdata, ch); + + if (string_ascii_begin <= i) + { + if (byte_ascii_p (ch)) + { + string_ascii_begin = i; + } + else + { + string_ascii_begin = MAX_STRING_ASCII_BEGIN; + } + } + } + + XSET_STRING_ASCII_BEGIN (sequence, min (string_ascii_begin, + MAX_STRING_ASCII_BEGIN)); + bump_string_modiff (sequence); + sledgehammer_check_ascii_begin (sequence); + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *v = XBIT_VECTOR (sequence); + sequence_len = bit_vector_length (v); + + 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); + + for (i = 0; i < sequence_len; ++i) + { + set_bit_vector_bit (v, i, XINT (sequence_carray [i])); + } + } + + return sequence; +} /************************************************************************/ /* property-list functions */ @@ -3124,69 +3779,121 @@ } -DEFUN ("fillarray", Ffillarray, 2, 2, 0, /* -Destructively modify ARRAY by replacing each element with ITEM. -ARRAY is a vector, bit vector, or string. +DEFUN ("fill", Ffill, 2, MANY, 0, /* +Destructively modify SEQUENCE by replacing each element with ITEM. +SEQUENCE is a list, vector, bit vector, or string. + +Optional keyword START is the index of the first element of SEQUENCE +to be modified, and defaults to zero. Optional keyword END is the +exclusive upper bound on the elements of SEQUENCE to be modified, and +defaults to the length of SEQUENCE. + +arguments: (SEQUENCE ITEM &key (START 0) END) */ - (array, item)) + (int nargs, Lisp_Object *args)) { - retry: - if (STRINGP (array)) + Lisp_Object sequence = args[0]; + Lisp_Object item = args[1]; + Elemcount starting = 0, ending = EMACS_INT_MAX, ii; + + PARSE_KEYWORDS (Qfill, nargs, args, 2, 2, (start, end), + (start = Qzero, end = Qunbound), 0); + + CHECK_NATNUM (start); + starting = XINT (start); + + if (!UNBOUNDP (end)) { - Bytecount old_bytecount = XSTRING_LENGTH (array); - Bytecount new_bytecount; - Bytecount item_bytecount; + CHECK_NATNUM (end); + ending = XINT (end); + } + + retry: + if (STRINGP (sequence)) + { + Bytecount old_bytecount, new_bytecount, item_bytecount; Ibyte item_buf[MAX_ICHAR_LEN]; Ibyte *p; - Ibyte *end; + Ibyte *pend; CHECK_CHAR_COERCE_INT (item); - CHECK_LISP_WRITEABLE (array); - sledgehammer_check_ascii_begin (array); + CHECK_LISP_WRITEABLE (sequence); + sledgehammer_check_ascii_begin (sequence); item_bytecount = set_itext_ichar (item_buf, XCHAR (item)); - new_bytecount = item_bytecount * (Bytecount) string_char_length (array); - - resize_string (array, -1, new_bytecount - old_bytecount); - - for (p = XSTRING_DATA (array), end = p + new_bytecount; - p < end; - p += item_bytecount) + + p = XSTRING_DATA (sequence); + p = (Ibyte *) itext_n_addr (p, starting); + old_bytecount = p - XSTRING_DATA (sequence); + + ending = min (ending, string_char_length (sequence)); + pend = (Ibyte *) itext_n_addr (p, ending - starting); + + new_bytecount = old_bytecount + (item_bytecount * (ending - starting)); + resize_string (sequence, -1, new_bytecount - old_bytecount); + + for (; p < pend; p += item_bytecount) memcpy (p, item_buf, item_bytecount); *p = '\0'; - XSET_STRING_ASCII_BEGIN (array, + XSET_STRING_ASCII_BEGIN (sequence, item_bytecount == 1 ? min (new_bytecount, MAX_STRING_ASCII_BEGIN) : 0); - bump_string_modiff (array); - sledgehammer_check_ascii_begin (array); + bump_string_modiff (sequence); + sledgehammer_check_ascii_begin (sequence); } - else if (VECTORP (array)) + else if (VECTORP (sequence)) { - Lisp_Object *p = XVECTOR_DATA (array); - Elemcount len = XVECTOR_LENGTH (array); - CHECK_LISP_WRITEABLE (array); - while (len--) - *p++ = item; + Lisp_Object *p = XVECTOR_DATA (sequence); + CHECK_LISP_WRITEABLE (sequence); + + ending = min (ending, XVECTOR_LENGTH (sequence)); + for (ii = starting; ii < ending; ++ii) + { + p[ii] = item; + } } - else if (BIT_VECTORP (array)) + else if (BIT_VECTORP (sequence)) { - Lisp_Bit_Vector *v = XBIT_VECTOR (array); - Elemcount len = bit_vector_length (v); + Lisp_Bit_Vector *v = XBIT_VECTOR (sequence); int bit; CHECK_BIT (item); bit = XINT (item); - CHECK_LISP_WRITEABLE (array); - while (len--) - set_bit_vector_bit (v, len, bit); + CHECK_LISP_WRITEABLE (sequence); + + ending = min (ending, bit_vector_length (v)); + for (ii = starting; ii < ending; ++ii) + { + set_bit_vector_bit (v, ii, bit); + } + } + else if (LISTP (sequence)) + { + Elemcount counting = 0; + + EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (counting >= starting) + { + if (counting < ending) + { + XSETCAR (tail, item); + } + else if (counting == ending) + { + break; + } + } + ++counting; + } } else { - array = wrong_type_argument (Qarrayp, array); + sequence = wrong_type_argument (Qsequencep, sequence); goto retry; } - return array; + return sequence; } Lisp_Object @@ -4758,12 +5465,16 @@ INIT_LISP_OBJECT (bit_vector); DEFSYMBOL (Qstring_lessp); + DEFSYMBOL (Qsort); + DEFSYMBOL (Qmerge); + DEFSYMBOL (Qfill); DEFSYMBOL (Qidentity); DEFSYMBOL (Qvector); DEFSYMBOL (Qarray); DEFSYMBOL (Qstring); DEFSYMBOL (Qlist); DEFSYMBOL (Qbit_vector); + defsymbol (&QsortX, "sort*"); DEFSYMBOL (Qyes_or_no_p); @@ -4814,7 +5525,9 @@ DEFSUBR (Fremrassq); DEFSUBR (Fnreverse); DEFSUBR (Freverse); - DEFSUBR (Fsort); + DEFSUBR (FsortX); + Ffset (intern ("sort"), QsortX); + DEFSUBR (Fmerge); DEFSUBR (Fplists_eq); DEFSUBR (Fplists_equal); DEFSUBR (Flax_plists_eq); @@ -4839,7 +5552,9 @@ DEFSUBR (Fequal); DEFSUBR (Fequalp); DEFSUBR (Fold_equal); - DEFSUBR (Ffillarray); + DEFSUBR (Ffill); + Ffset (intern ("fillarray"), Qfill); + DEFSUBR (Fnconc); DEFSUBR (FmapcarX); DEFSUBR (Fmapvector);