diff options
| author | Mattias EngdegÄrd | 2022-01-26 12:30:39 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2022-07-07 17:43:21 +0200 |
| commit | 53c0690fa28f338071703f1567d2d1c4054416f0 (patch) | |
| tree | df9781443fed41354470ba90b589b3e4adfbef39 /src | |
| parent | 9cd72b02b67e92e89b83791b66fe40c4b50d8357 (diff) | |
| download | emacs-53c0690fa28f338071703f1567d2d1c4054416f0.tar.gz emacs-53c0690fa28f338071703f1567d2d1c4054416f0.zip | |
Faster append and vconcat
By separating the code paths for append and vconcat, each becomes
simpler and faster.
* src/fns.c (concat_strings): Rename to...
(concat_to_string): ...this.
(concat): Split into concat_to_list and concat_to_vector.
(concat_to_list, concat_to_vector): New, specialised and
streamlined from earlier combined code.
(concat2, concat3, Fappend, Fconcat, Fvconcat): Adjust calls.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 225 |
1 files changed, 139 insertions, 86 deletions
| @@ -589,20 +589,21 @@ Do NOT use this function to compare file names for equality. */) | |||
| 589 | #endif /* !__STDC_ISO_10646__, !WINDOWSNT */ | 589 | #endif /* !__STDC_ISO_10646__, !WINDOWSNT */ |
| 590 | } | 590 | } |
| 591 | 591 | ||
| 592 | static Lisp_Object concat (ptrdiff_t nargs, Lisp_Object *args, | 592 | static Lisp_Object concat_to_list (ptrdiff_t nargs, Lisp_Object *args, |
| 593 | Lisp_Object last_tail, bool vector_target); | 593 | Lisp_Object last_tail); |
| 594 | static Lisp_Object concat_strings (ptrdiff_t nargs, Lisp_Object *args); | 594 | static Lisp_Object concat_to_vector (ptrdiff_t nargs, Lisp_Object *args); |
| 595 | static Lisp_Object concat_to_string (ptrdiff_t nargs, Lisp_Object *args); | ||
| 595 | 596 | ||
| 596 | Lisp_Object | 597 | Lisp_Object |
| 597 | concat2 (Lisp_Object s1, Lisp_Object s2) | 598 | concat2 (Lisp_Object s1, Lisp_Object s2) |
| 598 | { | 599 | { |
| 599 | return concat_strings (2, ((Lisp_Object []) {s1, s2})); | 600 | return concat_to_string (2, ((Lisp_Object []) {s1, s2})); |
| 600 | } | 601 | } |
| 601 | 602 | ||
| 602 | Lisp_Object | 603 | Lisp_Object |
| 603 | concat3 (Lisp_Object s1, Lisp_Object s2, Lisp_Object s3) | 604 | concat3 (Lisp_Object s1, Lisp_Object s2, Lisp_Object s3) |
| 604 | { | 605 | { |
| 605 | return concat_strings (3, ((Lisp_Object []) {s1, s2, s3})); | 606 | return concat_to_string (3, ((Lisp_Object []) {s1, s2, s3})); |
| 606 | } | 607 | } |
| 607 | 608 | ||
| 608 | DEFUN ("append", Fappend, Sappend, 0, MANY, 0, | 609 | DEFUN ("append", Fappend, Sappend, 0, MANY, 0, |
| @@ -615,7 +616,7 @@ usage: (append &rest SEQUENCES) */) | |||
| 615 | { | 616 | { |
| 616 | if (nargs == 0) | 617 | if (nargs == 0) |
| 617 | return Qnil; | 618 | return Qnil; |
| 618 | return concat (nargs - 1, args, args[nargs - 1], false); | 619 | return concat_to_list (nargs - 1, args, args[nargs - 1]); |
| 619 | } | 620 | } |
| 620 | 621 | ||
| 621 | DEFUN ("concat", Fconcat, Sconcat, 0, MANY, 0, | 622 | DEFUN ("concat", Fconcat, Sconcat, 0, MANY, 0, |
| @@ -628,7 +629,7 @@ to be `eq'. | |||
| 628 | usage: (concat &rest SEQUENCES) */) | 629 | usage: (concat &rest SEQUENCES) */) |
| 629 | (ptrdiff_t nargs, Lisp_Object *args) | 630 | (ptrdiff_t nargs, Lisp_Object *args) |
| 630 | { | 631 | { |
| 631 | return concat_strings (nargs, args); | 632 | return concat_to_string (nargs, args); |
| 632 | } | 633 | } |
| 633 | 634 | ||
| 634 | DEFUN ("vconcat", Fvconcat, Svconcat, 0, MANY, 0, | 635 | DEFUN ("vconcat", Fvconcat, Svconcat, 0, MANY, 0, |
| @@ -638,7 +639,7 @@ Each argument may be a list, vector or string. | |||
| 638 | usage: (vconcat &rest SEQUENCES) */) | 639 | usage: (vconcat &rest SEQUENCES) */) |
| 639 | (ptrdiff_t nargs, Lisp_Object *args) | 640 | (ptrdiff_t nargs, Lisp_Object *args) |
| 640 | { | 641 | { |
| 641 | return concat (nargs, args, Qnil, true); | 642 | return concat_to_vector (nargs, args); |
| 642 | } | 643 | } |
| 643 | 644 | ||
| 644 | 645 | ||
| @@ -706,8 +707,8 @@ the same empty object instead of its copy. */) | |||
| 706 | wrong_type_argument (Qsequencep, arg); | 707 | wrong_type_argument (Qsequencep, arg); |
| 707 | } | 708 | } |
| 708 | 709 | ||
| 709 | /* This structure holds information of an argument of `concat_strings' that is | 710 | /* This structure holds information of an argument of `concat_to_string' |
| 710 | a string and has text properties to be copied. */ | 711 | that is a string and has text properties to be copied. */ |
| 711 | struct textprop_rec | 712 | struct textprop_rec |
| 712 | { | 713 | { |
| 713 | ptrdiff_t argnum; /* refer to ARGS (arguments of `concat') */ | 714 | ptrdiff_t argnum; /* refer to ARGS (arguments of `concat') */ |
| @@ -716,7 +717,7 @@ struct textprop_rec | |||
| 716 | }; | 717 | }; |
| 717 | 718 | ||
| 718 | static Lisp_Object | 719 | static Lisp_Object |
| 719 | concat_strings (ptrdiff_t nargs, Lisp_Object *args) | 720 | concat_to_string (ptrdiff_t nargs, Lisp_Object *args) |
| 720 | { | 721 | { |
| 721 | USE_SAFE_ALLOCA; | 722 | USE_SAFE_ALLOCA; |
| 722 | 723 | ||
| @@ -912,19 +913,100 @@ concat_strings (ptrdiff_t nargs, Lisp_Object *args) | |||
| 912 | return result; | 913 | return result; |
| 913 | } | 914 | } |
| 914 | 915 | ||
| 915 | /* Concatenate sequences into a list or vector. */ | 916 | /* Concatenate sequences into a list. */ |
| 917 | Lisp_Object | ||
| 918 | concat_to_list (ptrdiff_t nargs, Lisp_Object *args, Lisp_Object last_tail) | ||
| 919 | { | ||
| 920 | /* Copy the contents of the args into the result. */ | ||
| 921 | Lisp_Object result = Qnil; | ||
| 922 | Lisp_Object last = Qnil; /* Last cons in result if nonempty. */ | ||
| 923 | |||
| 924 | for (ptrdiff_t i = 0; i < nargs; i++) | ||
| 925 | { | ||
| 926 | Lisp_Object arg = args[i]; | ||
| 927 | /* List arguments are treated specially since this is the common case. */ | ||
| 928 | if (CONSP (arg)) | ||
| 929 | { | ||
| 930 | Lisp_Object head = Fcons (XCAR (arg), Qnil); | ||
| 931 | Lisp_Object prev = head; | ||
| 932 | arg = XCDR (arg); | ||
| 933 | FOR_EACH_TAIL (arg) | ||
| 934 | { | ||
| 935 | Lisp_Object next = Fcons (XCAR (arg), Qnil); | ||
| 936 | XSETCDR (prev, next); | ||
| 937 | prev = next; | ||
| 938 | } | ||
| 939 | CHECK_LIST_END (arg, arg); | ||
| 940 | if (NILP (result)) | ||
| 941 | result = head; | ||
| 942 | else | ||
| 943 | XSETCDR (last, head); | ||
| 944 | last = prev; | ||
| 945 | } | ||
| 946 | else if (NILP (arg)) | ||
| 947 | ; | ||
| 948 | else if (VECTORP (arg) || STRINGP (arg) | ||
| 949 | || BOOL_VECTOR_P (arg) || COMPILEDP (arg)) | ||
| 950 | { | ||
| 951 | ptrdiff_t arglen = XFIXNUM (Flength (arg)); | ||
| 952 | ptrdiff_t argindex_byte = 0; | ||
| 916 | 953 | ||
| 954 | /* Copy element by element. */ | ||
| 955 | for (ptrdiff_t argindex = 0; argindex < arglen; argindex++) | ||
| 956 | { | ||
| 957 | /* Fetch next element of `arg' arg into `elt', or break if | ||
| 958 | `arg' is exhausted. */ | ||
| 959 | Lisp_Object elt; | ||
| 960 | if (STRINGP (arg)) | ||
| 961 | { | ||
| 962 | int c; | ||
| 963 | if (STRING_MULTIBYTE (arg)) | ||
| 964 | { | ||
| 965 | ptrdiff_t char_idx = argindex; | ||
| 966 | c = fetch_string_char_advance_no_check (arg, &char_idx, | ||
| 967 | &argindex_byte); | ||
| 968 | } | ||
| 969 | else | ||
| 970 | c = SREF (arg, argindex); | ||
| 971 | elt = make_fixed_natnum (c); | ||
| 972 | } | ||
| 973 | else if (BOOL_VECTOR_P (arg)) | ||
| 974 | elt = bool_vector_ref (arg, argindex); | ||
| 975 | else | ||
| 976 | elt = AREF (arg, argindex); | ||
| 977 | |||
| 978 | /* Store this element into the result. */ | ||
| 979 | Lisp_Object node = Fcons (elt, Qnil); | ||
| 980 | if (NILP (result)) | ||
| 981 | result = node; | ||
| 982 | else | ||
| 983 | XSETCDR (last, node); | ||
| 984 | last = node; | ||
| 985 | } | ||
| 986 | } | ||
| 987 | else | ||
| 988 | wrong_type_argument (Qsequencep, arg); | ||
| 989 | } | ||
| 990 | |||
| 991 | if (NILP (result)) | ||
| 992 | result = last_tail; | ||
| 993 | else | ||
| 994 | XSETCDR (last, last_tail); | ||
| 995 | |||
| 996 | return result; | ||
| 997 | } | ||
| 998 | |||
| 999 | /* Concatenate sequences into a vector. */ | ||
| 917 | Lisp_Object | 1000 | Lisp_Object |
| 918 | concat (ptrdiff_t nargs, Lisp_Object *args, Lisp_Object last_tail, | 1001 | concat_to_vector (ptrdiff_t nargs, Lisp_Object *args) |
| 919 | bool vector_target) | ||
| 920 | { | 1002 | { |
| 921 | /* Check argument types and compute total length of arguments. */ | 1003 | /* Check argument types and compute total length of arguments. */ |
| 922 | EMACS_INT result_len = 0; | 1004 | EMACS_INT result_len = 0; |
| 923 | for (ptrdiff_t i = 0; i < nargs; i++) | 1005 | for (ptrdiff_t i = 0; i < nargs; i++) |
| 924 | { | 1006 | { |
| 925 | Lisp_Object arg = args[i]; | 1007 | Lisp_Object arg = args[i]; |
| 926 | if (!(CONSP (arg) || NILP (arg) || VECTORP (arg) || STRINGP (arg) | 1008 | if (!(VECTORP (arg) || CONSP (arg) || NILP (arg) || STRINGP (arg) |
| 927 | || COMPILEDP (arg) || BOOL_VECTOR_P (arg))) | 1009 | || BOOL_VECTOR_P (arg) || COMPILEDP (arg))) |
| 928 | wrong_type_argument (Qsequencep, arg); | 1010 | wrong_type_argument (Qsequencep, arg); |
| 929 | EMACS_INT len = XFIXNAT (Flength (arg)); | 1011 | EMACS_INT len = XFIXNAT (Flength (arg)); |
| 930 | result_len += len; | 1012 | result_len += len; |
| @@ -932,90 +1014,61 @@ concat (ptrdiff_t nargs, Lisp_Object *args, Lisp_Object last_tail, | |||
| 932 | memory_full (SIZE_MAX); | 1014 | memory_full (SIZE_MAX); |
| 933 | } | 1015 | } |
| 934 | 1016 | ||
| 935 | /* When the target is a list, return the tail directly if all other | 1017 | /* Create the output vector. */ |
| 936 | arguments are empty. */ | 1018 | Lisp_Object result = make_uninit_vector (result_len); |
| 937 | if (!vector_target && result_len == 0) | 1019 | Lisp_Object *dst = XVECTOR (result)->contents; |
| 938 | return last_tail; | ||
| 939 | |||
| 940 | /* Create the output object. */ | ||
| 941 | Lisp_Object result = vector_target | ||
| 942 | ? make_nil_vector (result_len) | ||
| 943 | : Fmake_list (make_fixnum (result_len), Qnil); | ||
| 944 | 1020 | ||
| 945 | /* Copy the contents of the args into the result. */ | 1021 | /* Copy the contents of the args into the result. */ |
| 946 | Lisp_Object tail = Qnil; | ||
| 947 | ptrdiff_t toindex = 0; | ||
| 948 | if (CONSP (result)) | ||
| 949 | { | ||
| 950 | tail = result; | ||
| 951 | toindex = -1; /* -1 in toindex is flag we are making a list */ | ||
| 952 | } | ||
| 953 | |||
| 954 | Lisp_Object prev = Qnil; | ||
| 955 | 1022 | ||
| 956 | for (ptrdiff_t i = 0; i < nargs; i++) | 1023 | for (ptrdiff_t i = 0; i < nargs; i++) |
| 957 | { | 1024 | { |
| 958 | ptrdiff_t arglen = 0; | ||
| 959 | ptrdiff_t argindex = 0; | ||
| 960 | ptrdiff_t argindex_byte = 0; | ||
| 961 | |||
| 962 | Lisp_Object arg = args[i]; | 1025 | Lisp_Object arg = args[i]; |
| 963 | if (!CONSP (arg)) | 1026 | if (VECTORP (arg)) |
| 964 | arglen = XFIXNUM (Flength (arg)); | ||
| 965 | |||
| 966 | /* Copy element by element. */ | ||
| 967 | while (1) | ||
| 968 | { | 1027 | { |
| 969 | /* Fetch next element of `arg' arg into `elt', or break if | 1028 | ptrdiff_t size = ASIZE (arg); |
| 970 | `arg' is exhausted. */ | 1029 | memcpy (dst, XVECTOR (arg)->contents, size * sizeof *dst); |
| 971 | Lisp_Object elt; | 1030 | dst += size; |
| 972 | if (CONSP (arg)) | 1031 | } |
| 973 | { | 1032 | else if (CONSP (arg)) |
| 974 | elt = XCAR (arg); | 1033 | do |
| 975 | arg = XCDR (arg); | 1034 | { |
| 976 | } | 1035 | *dst++ = XCAR (arg); |
| 977 | else if (NILP (arg) || argindex >= arglen) | 1036 | arg = XCDR (arg); |
| 978 | break; | 1037 | } |
| 979 | else if (STRINGP (arg)) | 1038 | while (!NILP (arg)); |
| 1039 | else if (NILP (arg)) | ||
| 1040 | ; | ||
| 1041 | else if (STRINGP (arg)) | ||
| 1042 | { | ||
| 1043 | ptrdiff_t size = SCHARS (arg); | ||
| 1044 | if (STRING_MULTIBYTE (arg)) | ||
| 980 | { | 1045 | { |
| 981 | int c; | 1046 | ptrdiff_t byte = 0; |
| 982 | if (STRING_MULTIBYTE (arg)) | 1047 | for (ptrdiff_t i = 0; i < size;) |
| 983 | c = fetch_string_char_advance_no_check (arg, &argindex, | ||
| 984 | &argindex_byte); | ||
| 985 | else | ||
| 986 | { | 1048 | { |
| 987 | c = SREF (arg, argindex); | 1049 | int c = fetch_string_char_advance_no_check (arg, &i, &byte); |
| 988 | argindex++; | 1050 | *dst++ = make_fixnum (c); |
| 989 | } | 1051 | } |
| 990 | XSETFASTINT (elt, c); | ||
| 991 | } | ||
| 992 | else if (BOOL_VECTOR_P (arg)) | ||
| 993 | { | ||
| 994 | elt = bool_vector_ref (arg, argindex); | ||
| 995 | argindex++; | ||
| 996 | } | ||
| 997 | else | ||
| 998 | { | ||
| 999 | elt = AREF (arg, argindex); | ||
| 1000 | argindex++; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | /* Store this element into the result. */ | ||
| 1004 | if (toindex < 0) | ||
| 1005 | { | ||
| 1006 | XSETCAR (tail, elt); | ||
| 1007 | prev = tail; | ||
| 1008 | tail = XCDR (tail); | ||
| 1009 | } | 1052 | } |
| 1010 | else | 1053 | else |
| 1011 | { | 1054 | for (ptrdiff_t i = 0; i < size; i++) |
| 1012 | ASET (result, toindex, elt); | 1055 | *dst++ = make_fixnum (SREF (arg, i)); |
| 1013 | toindex++; | 1056 | } |
| 1014 | } | 1057 | else if (BOOL_VECTOR_P (arg)) |
| 1058 | { | ||
| 1059 | ptrdiff_t size = bool_vector_size (arg); | ||
| 1060 | for (ptrdiff_t i = 0; i < size; i++) | ||
| 1061 | *dst++ = bool_vector_ref (arg, i); | ||
| 1062 | } | ||
| 1063 | else | ||
| 1064 | { | ||
| 1065 | eassert (COMPILEDP (arg)); | ||
| 1066 | ptrdiff_t size = PVSIZE (arg); | ||
| 1067 | memcpy (dst, XVECTOR (arg)->contents, size * sizeof *dst); | ||
| 1068 | dst += size; | ||
| 1015 | } | 1069 | } |
| 1016 | } | 1070 | } |
| 1017 | if (!NILP (prev)) | 1071 | eassert (dst == XVECTOR (result)->contents + result_len); |
| 1018 | XSETCDR (prev, last_tail); | ||
| 1019 | 1072 | ||
| 1020 | return result; | 1073 | return result; |
| 1021 | } | 1074 | } |